完善前端项目结构和依赖配置

- 更新 package.json 中的依赖版本号,包括 element-plus、axios、pinia、vue-router 等
- 删除 HelloWorld 组件,替换为 router-view 以支持路由功能
- 添加 auth、category、customer、dashboard、medicine、role、stock、stockIn、stockOut、
  supplier、user 等多个 API 接口文件
- 创建权限指令、主布局组件和全局样式
- 集成 Element Plus UI 框架并注册图标组件
- 实现应用状态管理和用户状态管理 Store
- 配置 Vue Router 路由系统,支持动态菜单路由生成
- 移除旧的 CSS 样式文件,改用 SCSS 全局样式
```
This commit is contained in:
2026-07-06 12:07:23 +08:00
parent 3fcf927aef
commit 15da4fe492
38 changed files with 2057 additions and 409 deletions

View File

@@ -0,0 +1,72 @@
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { getUserList, createUser, updateUser, deleteUser, assignRoles } from '@/api/user'
import { getAllRoles } from '@/api/role'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Search, Refresh } from '@element-plus/icons-vue'
const query = reactive({ page: 1, size: 10, username: '' })
const tableData = ref([])
const total = ref(0)
const dialogVisible = ref(false)
const dialogTitle = ref('新增用户')
const form = reactive({ id: null, username: '', password: '', realName: '', phone: '', email: '', status: 1 })
const roleVisible = ref(false)
const roleForm = reactive({ userId: null, roleIds: [] })
const allRoles = ref([])
async function loadData() { try { const r = await getUserList(query); tableData.value = r.data?.records || []; total.value = r.data?.total || 0 } catch { /* */ } }
function handleAdd() { dialogTitle.value = '新增用户'; Object.keys(form).forEach(k => form[k] = k === 'status' ? 1 : ''); form.id = null; dialogVisible.value = true }
function handleEdit(row) { dialogTitle.value = '编辑用户'; Object.assign(form, row); form.password = ''; dialogVisible.value = true }
async function handleDelete(row) { await ElMessageBox.confirm('确定删除?', '确认', { type: 'warning' }); await deleteUser(row.id); ElMessage.success('删除成功'); loadData() }
async function handleSubmit() { const d = { ...form }; delete d.id; if (!d.password) delete d.password; if (form.id) { await updateUser(form.id, d); ElMessage.success('编辑成功') } else { await createUser(d); ElMessage.success('新增成功') }; dialogVisible.value = false; loadData() }
async function handleRole(row) { roleForm.userId = row.id; roleForm.roleIds = row.roleIds || []; roleVisible.value = true }
async function saveRoles() { await assignRoles(roleForm.userId, { roleIds: roleForm.roleIds }); ElMessage.success('角色分配成功'); roleVisible.value = false; loadData() }
onMounted(async () => { try { allRoles.value = (await getAllRoles()).data || [] } catch { /* */ }; loadData() })
</script>
<template>
<div class="page-container">
<div class="search-bar">
<el-input v-model="query.username" placeholder="用户名" clearable />
<el-button type="primary" :icon="Search" @click="loadData">搜索</el-button>
<el-button :icon="Refresh" @click="query.username='';loadData()">重置</el-button>
</div>
<div class="tool-bar">
<el-button type="primary" v-permission="'user:add'" @click="handleAdd">+ 新增用户</el-button>
</div>
<el-table :data="tableData" border stripe>
<el-table-column prop="username" label="用户名" width="120" />
<el-table-column prop="realName" label="姓名" width="100" />
<el-table-column prop="phone" label="手机号" width="130" />
<el-table-column prop="email" label="邮箱" width="180" show-overflow-tooltip />
<el-table-column prop="status" label="状态" width="80" align="center"><template #default="{row}"><el-tag :type="row.status===1?'success':'danger'" size="small">{{row.status===1?'启用':'停用'}}</el-tag></template></el-table-column>
<el-table-column label="操作" width="220" fixed="right">
<template #default="{row}"><div class="table-actions"><el-button size="small" v-permission="'user:edit'" @click="handleEdit(row)">编辑</el-button><el-button size="small" v-permission="'user:assign-role'" @click="handleRole(row)">角色</el-button><el-button size="small" type="danger" v-permission="'user:delete'" @click="handleDelete(row)">删除</el-button></div></template>
</el-table-column>
</el-table>
<div style="display:flex;justify-content:flex-end;margin-top:16px"><el-pagination v-model:current-page="query.page" v-model:page-size="query.size" :total="total" :page-sizes="[10,20,50]" layout="total,sizes,prev,pager,next" @current-change="loadData" @size-change="query.page=1;loadData()" /></div>
<!-- 用户弹窗 -->
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="500px">
<el-form :model="form" label-width="80px">
<el-form-item label="用户名" required><el-input v-model="form.username" /></el-form-item>
<el-form-item label="密码" :required="!form.id"><el-input v-model="form.password" type="password" show-password :placeholder="form.id?'留空则不修改':''" /></el-form-item>
<el-form-item label="姓名"><el-input v-model="form.realName" /></el-form-item>
<el-form-item label="手机号"><el-input v-model="form.phone" /></el-form-item>
<el-form-item label="邮箱"><el-input v-model="form.email" /></el-form-item>
<el-form-item label="状态"><el-switch v-model="form.status" :active-value="1" :inactive-value="0" active-text="启用" inactive-text="停用" /></el-form-item>
</el-form>
<template #footer><el-button @click="dialogVisible=false">取消</el-button><el-button type="primary" @click="handleSubmit">确定</el-button></template>
</el-dialog>
<!-- 角色分配弹窗 -->
<el-dialog v-model="roleVisible" title="分配角色" width="450px">
<el-checkbox-group v-model="roleForm.roleIds">
<el-checkbox v-for="r in allRoles" :key="r.id" :label="r.id" :value="r.id">{{ r.name }}</el-checkbox>
</el-checkbox-group>
<template #footer><el-button @click="roleVisible=false">取消</el-button><el-button type="primary" @click="saveRoles">保存</el-button></template>
</el-dialog>
</div>
</template>