| | |
| | | <script setup lang="ts"> |
| | | import { computed, onMounted, reactive, ref } from 'vue' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import { Plus, Refresh, Search } from '@element-plus/icons-vue' |
| | | import { createAdminStaff, deleteAdminStaff, getAdminRoles, getAdminStaff, resetAdminStaffPassword, updateAdminStaff } from '../api/admin' |
| | | import type { AdminRole, AdminStaff } from '../types/admin' |
| | | const loading = ref(false), visible = ref(false), saving = ref(false), editId = ref<number | string>() |
| | | const rows = ref<AdminStaff[]>([]), roles = ref<AdminRole[]>([]) |
| | | const filters = reactive({ name: '', mobile: '', role: '', status: '' }) |
| | | const form = reactive({ displayName: '', mobile: '', account: '', password: '', confirmPassword: '', roleCode: '', positionName: '', customerWechat: '', status: 1 }) |
| | | const filtered = computed(() => rows.value.filter(r => (!filters.name || r.displayName.includes(filters.name)) && (!filters.mobile || (r.mobile || '').includes(filters.mobile)) && (!filters.role || r.roleCode === filters.role) && (!filters.status || String(r.status) === filters.status))) |
| | | const roleName = (code: string) => roles.value.find(r => r.roleCode === code)?.roleName || code |
| | | const formatDateTime = (value?: string) => value ? value.replace('T', ' ').slice(0, 19) : '' |
| | | const isDefaultAdmin = (row: AdminStaff) => row.account === 'admin' |
| | | const editingDefaultAdmin = computed(() => rows.value.some(row => String(row.id) === String(editId.value) && isDefaultAdmin(row))) |
| | | const keepMobileDigits = () => { form.mobile = form.mobile.replace(/\D/g, '').slice(0, 11) } |
| | | const keepAccountCharacters = () => { form.account = form.account.replace(/[^A-Za-z0-9]/g, '').slice(0, 32) } |
| | | async function load() { loading.value = true; try { const result = await Promise.all([getAdminStaff(), getAdminRoles()]); rows.value = result[0]; roles.value = result[1].filter(r => r.status === 1) } finally { loading.value = false } } |
| | | function reset() { Object.assign(filters, { name: '', mobile: '', role: '', status: '' }) } |
| | | function create() { editId.value = undefined; Object.assign(form, { displayName: '', mobile: '', account: '', password: '', confirmPassword: '', roleCode: roles.value[0]?.roleCode || '', positionName: '', customerWechat: '', status: 1 }); visible.value = true } |
| | | function edit(row: AdminStaff) { editId.value = row.id; Object.assign(form, { displayName: row.displayName, mobile: row.mobile || '', account: row.account, password: '', confirmPassword: '', roleCode: row.roleCode, positionName: row.positionName || '', customerWechat: row.customerWechat || '', status: row.status }); visible.value = true } |
| | | async function save() { if (!form.displayName.trim() || !form.account.trim() || !form.roleCode || !form.positionName.trim()) return ElMessage.warning('请填写人员名称、登录账号、角色和职务'); if (!editId.value && form.password.length < 6) return ElMessage.warning('新建员工密码至少 6 位'); if (!editId.value && form.password !== form.confirmPassword) return ElMessage.warning('两次输入的密码不一致'); saving.value = true; try { const { confirmPassword, ...formPayload } = form; const payload = { ...formPayload, password: form.password || undefined }; if (editId.value) await updateAdminStaff(editId.value, payload); else await createAdminStaff({ ...payload, password: form.password }); ElMessage.success('保存成功'); visible.value = false; load() } finally { saving.value = false } } |
| | | async function changeStatus(row: AdminStaff) { await updateAdminStaff(row.id, { displayName: row.displayName, mobile: row.mobile || '', account: row.account, roleCode: row.roleCode, positionName: row.positionName || '未设置', customerWechat: row.customerWechat || '', status: row.status === 1 ? 0 : 1 }); ElMessage.success(row.status === 1 ? '已禁用' : '已启用'); load() } |
| | | async function password(row: AdminStaff) { const result = await ElMessageBox.prompt('请输入 6-64 位新密码', `重置 ${row.displayName} 的密码`, { inputType: 'password', inputPattern: /^.{6,64}$/, inputErrorMessage: '密码长度为 6-64 位' }); await resetAdminStaffPassword(row.id, result.value); ElMessage.success('密码已重置') } |
| | | async function remove(row: AdminStaff) { await ElMessageBox.confirm(`确认删除员工“${row.displayName}”?`, '删除员工', { type: 'warning' }); await deleteAdminStaff(row.id); ElMessage.success('已删除'); load() } |
| | | onMounted(load) |
| | | import { computed, onMounted, reactive, ref } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import { Plus, Refresh, Search } from "@element-plus/icons-vue"; |
| | | import { |
| | | createAdminStaff, |
| | | deleteAdminStaff, |
| | | getAdminRoles, |
| | | getAdminStaff, |
| | | resetAdminStaffPassword, |
| | | updateAdminStaff, |
| | | } from "../api/admin"; |
| | | import type { AdminRole, AdminStaff } from "../types/admin"; |
| | | const loading = ref(false), |
| | | visible = ref(false), |
| | | saving = ref(false), |
| | | editId = ref<number | string>(); |
| | | const rows = ref<AdminStaff[]>([]), |
| | | roles = ref<AdminRole[]>([]); |
| | | const filters = reactive({ name: "", mobile: "", role: "", status: "" }); |
| | | const form = reactive({ |
| | | displayName: "", |
| | | mobile: "", |
| | | account: "", |
| | | password: "", |
| | | confirmPassword: "", |
| | | roleCode: "", |
| | | positionName: "", |
| | | customerWechat: "", |
| | | status: 1, |
| | | }); |
| | | const filtered = computed(() => |
| | | rows.value.filter( |
| | | (r) => |
| | | (!filters.name || r.displayName.includes(filters.name)) && |
| | | (!filters.mobile || (r.mobile || "").includes(filters.mobile)) && |
| | | (!filters.role || r.roleCode === filters.role) && |
| | | (!filters.status || String(r.status) === filters.status), |
| | | ), |
| | | ); |
| | | const pagination = reactive({ page: 1, size: 10 }); |
| | | const pagedRows = computed(() => |
| | | filtered.value.slice( |
| | | (pagination.page - 1) * pagination.size, |
| | | pagination.page * pagination.size, |
| | | ), |
| | | ); |
| | | const roleName = (code: string) => |
| | | roles.value.find((r) => r.roleCode === code)?.roleName || code; |
| | | const formatDateTime = (value?: string) => |
| | | value ? value.replace("T", " ").slice(0, 19) : ""; |
| | | const isDefaultAdmin = (row: AdminStaff) => row.account === "admin"; |
| | | const editingDefaultAdmin = computed(() => |
| | | rows.value.some( |
| | | (row) => String(row.id) === String(editId.value) && isDefaultAdmin(row), |
| | | ), |
| | | ); |
| | | const keepMobileDigits = () => { |
| | | form.mobile = form.mobile.replace(/\D/g, "").slice(0, 11); |
| | | }; |
| | | const keepAccountCharacters = () => { |
| | | form.account = form.account.replace(/[^A-Za-z0-9]/g, "").slice(0, 32); |
| | | }; |
| | | async function load() { |
| | | loading.value = true; |
| | | try { |
| | | const result = await Promise.all([getAdminStaff(), getAdminRoles()]); |
| | | rows.value = result[0]; |
| | | roles.value = result[1].filter((r) => r.status === 1); |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | } |
| | | function reset() { |
| | | Object.assign(filters, { name: "", mobile: "", role: "", status: "" }); |
| | | pagination.page = 1; |
| | | } |
| | | function create() { |
| | | editId.value = undefined; |
| | | Object.assign(form, { |
| | | displayName: "", |
| | | mobile: "", |
| | | account: "", |
| | | password: "", |
| | | confirmPassword: "", |
| | | roleCode: roles.value[0]?.roleCode || "", |
| | | positionName: "", |
| | | customerWechat: "", |
| | | status: 1, |
| | | }); |
| | | visible.value = true; |
| | | } |
| | | function edit(row: AdminStaff) { |
| | | editId.value = row.id; |
| | | Object.assign(form, { |
| | | displayName: row.displayName, |
| | | mobile: row.mobile || "", |
| | | account: row.account, |
| | | password: "", |
| | | confirmPassword: "", |
| | | roleCode: row.roleCode, |
| | | positionName: row.positionName || "", |
| | | customerWechat: row.customerWechat || "", |
| | | status: row.status, |
| | | }); |
| | | visible.value = true; |
| | | } |
| | | async function save() { |
| | | if ( |
| | | !form.displayName.trim() || |
| | | !form.account.trim() || |
| | | !form.roleCode || |
| | | !form.positionName.trim() |
| | | ) |
| | | return ElMessage.warning("请填写人员名称、登录账号、角色和职务"); |
| | | if (!editId.value && form.password.length < 6) |
| | | return ElMessage.warning("新建员工密码至少 6 位"); |
| | | if (!editId.value && form.password !== form.confirmPassword) |
| | | return ElMessage.warning("两次输入的密码不一致"); |
| | | saving.value = true; |
| | | try { |
| | | const { confirmPassword, ...formPayload } = form; |
| | | const payload = { ...formPayload, password: form.password || undefined }; |
| | | if (editId.value) await updateAdminStaff(editId.value, payload); |
| | | else await createAdminStaff({ ...payload, password: form.password }); |
| | | ElMessage.success("保存成功"); |
| | | visible.value = false; |
| | | load(); |
| | | } finally { |
| | | saving.value = false; |
| | | } |
| | | } |
| | | async function changeStatus(row: AdminStaff) { |
| | | await updateAdminStaff(row.id, { |
| | | displayName: row.displayName, |
| | | mobile: row.mobile || "", |
| | | account: row.account, |
| | | roleCode: row.roleCode, |
| | | positionName: row.positionName || "未设置", |
| | | customerWechat: row.customerWechat || "", |
| | | status: row.status === 1 ? 0 : 1, |
| | | }); |
| | | ElMessage.success(row.status === 1 ? "已禁用" : "已启用"); |
| | | load(); |
| | | } |
| | | async function password(row: AdminStaff) { |
| | | const result = await ElMessageBox.prompt( |
| | | "请输入 6-64 位新密码", |
| | | `重置 ${row.displayName} 的密码`, |
| | | { |
| | | inputType: "password", |
| | | inputPattern: /^.{6,64}$/, |
| | | inputErrorMessage: "密码长度为 6-64 位", |
| | | }, |
| | | ); |
| | | await resetAdminStaffPassword(row.id, result.value); |
| | | ElMessage.success("密码已重置"); |
| | | } |
| | | async function remove(row: AdminStaff) { |
| | | await ElMessageBox.confirm(`确认删除员工“${row.displayName}”?`, "删除员工", { |
| | | type: "warning", |
| | | }); |
| | | await deleteAdminStaff(row.id); |
| | | ElMessage.success("已删除"); |
| | | load(); |
| | | } |
| | | onMounted(load); |
| | | </script> |
| | | <template><div class="page-stack"><section class="panel"><div class="panel-header"><h3>账号</h3></div><el-form class="filters" inline><el-form-item label="用户名称"><el-input v-model="filters.name" /></el-form-item><el-form-item label="用户电话"><el-input v-model="filters.mobile" /></el-form-item><el-form-item label="角色"><el-select v-model="filters.role" clearable placeholder="请选择"><el-option v-for="role in roles" :key="role.id" :label="role.roleName" :value="role.roleCode" /></el-select></el-form-item><el-form-item label="账号状态"><el-select v-model="filters.status" clearable placeholder="请选择"><el-option label="启用" value="1" /><el-option label="禁用" value="0" /></el-select></el-form-item><el-form-item><el-button :icon="Refresh" @click="reset">重置</el-button><el-button type="primary" :icon="Search">搜索</el-button></el-form-item></el-form><div class="tools"><el-button type="primary" :icon="Plus" @click="create">新增</el-button></div><el-table v-loading="loading" :data="filtered" stripe class="table"><el-table-column type="index" label="序号" width="60"/><el-table-column prop="displayName" label="人员名称" min-width="96"/><el-table-column prop="mobile" label="联系电话" min-width="118"/><el-table-column prop="account" label="登录账号" min-width="116"/><el-table-column label="角色" min-width="110"><template #default="{row}">{{ roleName(row.roleCode) }}</template></el-table-column><el-table-column prop="positionName" label="职务" min-width="92"/><el-table-column prop="customerWechat" label="客服微信" min-width="116"/><el-table-column label="状态" width="78"><template #default="{row}">{{row.status === 1 ? '启用' : '禁用'}}</template></el-table-column><el-table-column label="创建时间" min-width="155"><template #default="{ row }">{{ formatDateTime(row.createdAt) }}</template></el-table-column><el-table-column label="操作" width="225" fixed="right"><template #default="{row}"><el-button link type="primary" @click="edit(row)">编辑</el-button><el-button v-if="!isDefaultAdmin(row)" link type="primary" @click="changeStatus(row)">{{row.status === 1 ? '禁用' : '启用'}}</el-button><el-button link type="primary" @click="password(row)">重置密码</el-button><el-button v-if="!isDefaultAdmin(row)" link type="danger" @click="remove(row)">删除</el-button></template></el-table-column></el-table></section><el-dialog v-model="visible" :title="editId ? '编辑员工' : '新增员工'" width="560px" destroy-on-close><el-form label-width="92px"><el-form-item label="人员名称" required><el-input v-model="form.displayName"/></el-form-item><el-form-item label="联系电话"><el-input v-model="form.mobile" inputmode="numeric" maxlength="11" @input="keepMobileDigits"/></el-form-item><el-form-item label="登录账号" required><el-input v-model="form.account" :disabled="Boolean(editId)" maxlength="32" @input="keepAccountCharacters"/></el-form-item><el-form-item label="登录密码" :required="!editId"><el-input v-model="form.password" type="password" :placeholder="editId ? '留空则不修改' : '至少 6 位'"/></el-form-item><el-form-item v-if="!editId" label="确认密码" required><el-input v-model="form.confirmPassword" type="password" placeholder="请再次输入密码"/></el-form-item><el-form-item label="角色" required><el-select v-model="form.roleCode" class="full"><el-option v-for="role in roles" :key="role.id" :label="role.roleName" :value="role.roleCode"/></el-select></el-form-item><el-form-item label="职务" required><el-input v-model="form.positionName"/></el-form-item><el-form-item label="客服微信"><el-input v-model="form.customerWechat"/></el-form-item><el-form-item label="账号状态"><el-switch v-model="form.status" :disabled="editingDefaultAdmin" :active-value="1" :inactive-value="0" active-text="启用" inactive-text="禁用"/></el-form-item></el-form><template #footer><el-button @click="visible=false">取消</el-button><el-button type="primary" :loading="saving" @click="save">保存</el-button></template></el-dialog></div></template> |
| | | <style scoped>.filters{margin:14px 20px 0;padding:16px;border:1px solid #dce3ea;border-radius:4px}.filters :deep(.el-input),.filters :deep(.el-select){width:130px}.tools{display:flex;justify-content:flex-end;padding:10px 20px 14px}.table{margin:0 20px;width:calc(100% - 40px)}.full{width:100%}</style> |
| | | <template> |
| | | <div class="page-stack"> |
| | | <section class="panel"> |
| | | <div class="panel-header"><h3>账号</h3></div> |
| | | <el-form class="filters" inline |
| | | ><el-form-item label="用户名称" |
| | | ><el-input v-model="filters.name" /></el-form-item |
| | | ><el-form-item label="用户电话" |
| | | ><el-input v-model="filters.mobile" /></el-form-item |
| | | ><el-form-item label="角色" |
| | | ><el-select v-model="filters.role" clearable placeholder="请选择" |
| | | ><el-option |
| | | v-for="role in roles" |
| | | :key="role.id" |
| | | :label="role.roleName" |
| | | :value="role.roleCode" /></el-select></el-form-item |
| | | ><el-form-item label="账号状态" |
| | | ><el-select v-model="filters.status" clearable placeholder="请选择" |
| | | ><el-option label="启用" value="1" /><el-option |
| | | label="禁用" |
| | | value="0" /></el-select></el-form-item |
| | | ><el-form-item |
| | | ><el-button :icon="Refresh" @click="reset">重置</el-button |
| | | ><el-button type="primary" :icon="Search" |
| | | >搜索</el-button |
| | | ></el-form-item |
| | | ></el-form |
| | | > |
| | | <div class="tools"> |
| | | <el-button type="primary" :icon="Plus" @click="create">新增</el-button> |
| | | </div> |
| | | <el-table v-loading="loading" :data="pagedRows" stripe class="table" |
| | | ><el-table-column |
| | | type="index" |
| | | label="序号" |
| | | width="60" |
| | | /><el-table-column |
| | | prop="displayName" |
| | | label="人员名称" |
| | | min-width="96" |
| | | /><el-table-column |
| | | prop="mobile" |
| | | label="联系电话" |
| | | min-width="118" |
| | | /><el-table-column |
| | | prop="account" |
| | | label="登录账号" |
| | | min-width="116" |
| | | /><el-table-column label="角色" min-width="110" |
| | | ><template #default="{ row }">{{ |
| | | roleName(row.roleCode) |
| | | }}</template></el-table-column |
| | | ><el-table-column |
| | | prop="positionName" |
| | | label="职务" |
| | | min-width="92" |
| | | /><el-table-column |
| | | prop="customerWechat" |
| | | label="客服微信" |
| | | min-width="116" |
| | | /><el-table-column label="状态" width="78" |
| | | ><template #default="{ row }">{{ |
| | | row.status === 1 ? "启用" : "禁用" |
| | | }}</template></el-table-column |
| | | ><el-table-column label="创建时间" min-width="155" |
| | | ><template #default="{ row }">{{ |
| | | formatDateTime(row.createdAt) |
| | | }}</template></el-table-column |
| | | ><el-table-column label="操作" width="225" fixed="right" |
| | | ><template #default="{ row }" |
| | | ><el-button link type="primary" @click="edit(row)">编辑</el-button |
| | | ><el-button |
| | | v-if="!isDefaultAdmin(row)" |
| | | link |
| | | type="primary" |
| | | @click="changeStatus(row)" |
| | | >{{ row.status === 1 ? "禁用" : "启用" }}</el-button |
| | | ><el-button link type="primary" @click="password(row)" |
| | | >重置密码</el-button |
| | | ><el-button |
| | | v-if="!isDefaultAdmin(row)" |
| | | link |
| | | type="danger" |
| | | @click="remove(row)" |
| | | >删除</el-button |
| | | ></template |
| | | ></el-table-column |
| | | ></el-table |
| | | > |
| | | <div class="pagination-row"> |
| | | <el-pagination |
| | | v-model:current-page="pagination.page" |
| | | background |
| | | layout="total, prev, pager, next" |
| | | :page-size="pagination.size" |
| | | :total="filtered.length" |
| | | /> |
| | | </div> |
| | | </section> |
| | | <el-dialog |
| | | v-model="visible" |
| | | :title="editId ? '编辑员工' : '新增员工'" |
| | | width="560px" |
| | | destroy-on-close |
| | | ><el-form label-width="92px" |
| | | ><el-form-item label="人员名称" required |
| | | ><el-input v-model="form.displayName" /></el-form-item |
| | | ><el-form-item label="联系电话" |
| | | ><el-input |
| | | v-model="form.mobile" |
| | | inputmode="numeric" |
| | | maxlength="11" |
| | | @input="keepMobileDigits" /></el-form-item |
| | | ><el-form-item label="登录账号" required |
| | | ><el-input |
| | | v-model="form.account" |
| | | :disabled="Boolean(editId)" |
| | | maxlength="32" |
| | | @input="keepAccountCharacters" /></el-form-item |
| | | ><el-form-item label="登录密码" :required="!editId" |
| | | ><el-input |
| | | v-model="form.password" |
| | | type="password" |
| | | :placeholder=" |
| | | editId ? '留空则不修改' : '至少 6 位' |
| | | " /></el-form-item |
| | | ><el-form-item v-if="!editId" label="确认密码" required |
| | | ><el-input |
| | | v-model="form.confirmPassword" |
| | | type="password" |
| | | placeholder="请再次输入密码" /></el-form-item |
| | | ><el-form-item label="角色" required |
| | | ><el-select v-model="form.roleCode" class="full" |
| | | ><el-option |
| | | v-for="role in roles" |
| | | :key="role.id" |
| | | :label="role.roleName" |
| | | :value="role.roleCode" /></el-select></el-form-item |
| | | ><el-form-item label="职务" required |
| | | ><el-input v-model="form.positionName" /></el-form-item |
| | | ><el-form-item label="客服微信" |
| | | ><el-input v-model="form.customerWechat" /></el-form-item |
| | | ><el-form-item label="账号状态" |
| | | ><el-switch |
| | | v-model="form.status" |
| | | :disabled="editingDefaultAdmin" |
| | | :active-value="1" |
| | | :inactive-value="0" |
| | | active-text="启用" |
| | | inactive-text="禁用" /></el-form-item></el-form |
| | | ><template #footer |
| | | ><el-button @click="visible = false">取消</el-button |
| | | ><el-button type="primary" :loading="saving" @click="save" |
| | | >保存</el-button |
| | | ></template |
| | | ></el-dialog |
| | | > |
| | | </div> |
| | | </template> |
| | | <style scoped> |
| | | .filters { |
| | | margin: 14px 20px 0; |
| | | padding: 16px; |
| | | border: 1px solid #dce3ea; |
| | | border-radius: 4px; |
| | | } |
| | | .filters :deep(.el-input), |
| | | .filters :deep(.el-select) { |
| | | width: 130px; |
| | | } |
| | | .tools { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | padding: 10px 20px 14px; |
| | | } |
| | | .table { |
| | | margin: 0 20px; |
| | | width: calc(100% - 40px); |
| | | } |
| | | .full { |
| | | width: 100%; |
| | | } |
| | | </style> |