shinnosuke
2026-09-16 c5a63165aca6468157e0b4807d511e828de2076a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
<script setup lang="ts">
import { computed, onMounted, reactive, ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { Download, Edit, Plus, Refresh, Search, View } from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import {
  createAdminRole,
  createAdminStaff,
  deleteAdminRole,
  getAdminRoles,
  getAdminStaff,
  getUpgradeApplications,
  updateAdminRole,
  updateAdminRoleStatus,
  updateAdminStaff,
} from '../api/admin'
import type { AdminRole, AdminStaff } from '../types/admin'
import { TALENT_CARD_LEVELS } from '../utils/cardLevels'
import { downloadExcel } from '../utils/excel'
 
const route = useRoute()
const router = useRouter()
const kind = computed(() => String(route.meta.businessKind || 'upgrade'))
const isUpgrade = computed(() => kind.value === 'upgrade')
const isRole = computed(() => kind.value === 'role')
const title = computed(() => isUpgrade.value ? '升级申请' : isRole.value ? '角色管理' : '员工管理')
const loading = ref(false)
const rows = ref<Record<string, unknown>[]>([])
const dialogVisible = ref(false)
const editVisible = ref(false)
const formRef = ref<FormInstance>()
const activeRow = ref<Record<string, unknown>>()
const editingId = ref<number | string>()
const filters = reactive({ keyword: '', cardLevel: '', status: '', timeRange: [] as string[] })
const pagination = reactive({ page: 1, size: 10, total: 0 })
const form = reactive({ name: '', code: '', password: '', remark: '', status: 1 })
const accountPattern = /^[A-Za-z0-9]+$/
const rules = computed<FormRules>(() => ({
  name: [{ required: true, message: isRole.value ? '请输入角色名称' : '请输入员工姓名', trigger: 'blur' }],
  code: [
    { required: true, message: isRole.value ? '请输入角色编码' : '请输入登录账号', trigger: 'blur' },
    {
      validator: (_rule, value, callback) => !isRole.value && value && !accountPattern.test(String(value).trim())
        ? callback(new Error('登录账号仅支持英文、数字或英文+数字'))
        : callback(),
      trigger: 'blur',
    },
  ],
  password: [{
    validator: (_rule, value, callback) => !isRole.value && !editingId.value && !value
      ? callback(new Error('请设置登录密码'))
      : callback(),
    trigger: 'blur',
  }],
}))
 
const filteredRows = computed(() => isRole.value
  ? rows.value.filter((row) => (!filters.keyword || String(row.roleName || '').includes(filters.keyword)) && (!filters.status || String(row.status) === filters.status))
  : rows.value)
const pagedRows = computed(() => isUpgrade.value
  ? rows.value
  : filteredRows.value.slice((pagination.page - 1) * pagination.size, pagination.page * pagination.size))
const pageTotal = computed(() => isRole.value ? filteredRows.value.length : pagination.total)
 
const columns = computed(() => isUpgrade.value
  ? [['applicationNo', '申请编号'], ['talentName', '持卡人名称'], ['talentMobile', '持卡人手机号'], ['targetLevel', '卡片等级'], ['applyTime', '申请时间'], ['status', '状态']]
  : isRole.value
    ? [['serialNo', '序号'], ['roleName', '角色名称'], ['remark', '备注'], ['statusText', '状态'], ['createdAt', '创建时间']]
    : [['account', '登录账号'], ['displayName', '员工姓名'], ['roleCode', '所属角色'], ['statusText', '状态'], ['createdAt', '创建时间']])
 
function statusText(value: unknown) { return Number(value) === 1 ? '启用' : '停用' }
function formatDateTime(value: unknown) {
  return typeof value === 'string' && value ? value.replace('T', ' ').slice(0, 19) : ''
}
function normalizeRole(row: AdminRole, index: number) { return { ...row, serialNo: index + 1, createdAt: formatDateTime(row.createdAt), statusText: statusText(row.status) } }
function normalizeStaff(row: AdminStaff) { return { ...row, createdAt: formatDateTime(row.createdAt), statusText: statusText(row.status) } }
function normalizeUpgrade(row: Record<string, unknown>) { return { ...row, applyTime: formatDateTime(row.applyTime) } }
function isPlatformAdminRole(row: Record<string, unknown>) { return String(row.roleCode || '') === 'platform_admin' }
 
async function loadData() {
  loading.value = true
  try {
    if (isUpgrade.value) {
      const result = await getUpgradeApplications({ page: pagination.page, size: pagination.size, keyword: filters.keyword || undefined, targetLevel: filters.cardLevel || undefined, status: filters.status || undefined, applyStart: filters.timeRange[0], applyEnd: filters.timeRange[1] })
      rows.value = result.records.map((row) => normalizeUpgrade(row as unknown as Record<string, unknown>))
      pagination.total = result.total
    } else if (isRole.value) {
      rows.value = (await getAdminRoles()).map(normalizeRole)
      pagination.total = rows.value.length
    } else {
      rows.value = (await getAdminStaff()).map(normalizeStaff)
      pagination.total = rows.value.length
    }
  } finally { loading.value = false }
}
 
function search() { pagination.page = 1; loadData() }
function reset() { filters.keyword = ''; filters.cardLevel = ''; filters.status = ''; filters.timeRange = []; search() }
function changePage() { if (isUpgrade.value) loadData() }
function view(row: Record<string, unknown>) {
  if (isUpgrade.value) {
    router.push(`/upgrade-applications/${row.id}`)
    return
  }
  if (isRole.value) {
    router.push(`/roles/${row.id}/view`)
    return
  }
  activeRow.value = row
  dialogVisible.value = true
}
 
function openCreate() {
  if (isRole.value) { router.push('/roles/new'); return }
  editingId.value = undefined
  Object.assign(form, { name: '', code: '', password: '', remark: '', status: 1 })
  editVisible.value = true
}
 
function openEdit(row: Record<string, unknown>) {
  if (isRole.value) { router.push(`/roles/${row.id}`); return }
  editingId.value = row.id as number | string
  Object.assign(form, { name: String(row.roleName || row.displayName || ''), code: String(row.roleCode || row.account || ''), password: '', remark: String(row.remark || ''), status: Number(row.status) })
  editVisible.value = true
}
 
async function save() {
  const valid = await formRef.value?.validate().catch(() => false)
  if (!valid) return
  if (isRole.value) {
    const payload = { roleName: form.name.trim(), roleCode: form.code.trim(), remark: form.remark.trim(), status: form.status }
    if (editingId.value) await updateAdminRole(editingId.value, payload); else await createAdminRole(payload)
  } else {
    if (!editingId.value && !form.password.trim()) { ElMessage.warning('请输入登录密码'); return }
    const payload = { account: form.code.trim(), displayName: form.name.trim(), mobile: '', roleCode: '', positionName: '未设置', customerWechat: '', status: form.status, password: form.password || undefined }
    if (editingId.value) await updateAdminStaff(editingId.value, payload); else await createAdminStaff(payload as typeof payload & { password: string })
  }
  ElMessage.success('保存成功')
  editVisible.value = false
  loadData()
}
 
async function changeRoleStatus(row: Record<string, unknown>) {
  const status = Number(row.status) === 1 ? 0 : 1
  await updateAdminRoleStatus(row.id as number | string, status)
  ElMessage.success(status === 1 ? '已启用' : '已停用')
  await loadData()
}
 
async function removeRole(row: Record<string, unknown>) {
  await ElMessageBox.confirm(`确认删除角色“${row.roleName}”?`, '删除角色', { type: 'warning' })
  await deleteAdminRole(row.id as number | string)
  ElMessage.success('已删除')
  await loadData()
}
 
async function download() {
  let data = rows.value
  if (isUpgrade.value) {
    const result = await getUpgradeApplications({ page: 1, size: 1000, keyword: filters.keyword || undefined, targetLevel: filters.cardLevel || undefined, status: filters.status || undefined, applyStart: filters.timeRange[0], applyEnd: filters.timeRange[1] })
    data = result.records.map((row) => normalizeUpgrade(row as unknown as Record<string, unknown>))
  }
  downloadExcel(title.value, columns.value.map(([key, label]) => ({ title: label, value: (row: Record<string, unknown>) => {
    const value = row[key]
    return value === null || value === undefined ? '' : typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean' ? value : String(value)
  } })), data)
  ElMessage.success(`已导出 ${data.length} 条${title.value}数据`)
}
 
watch(() => route.fullPath, reset)
onMounted(loadData)
</script>
 
<template>
  <div class="page-stack prototype-page business-fill-page" :class="{ 'role-management-page': isRole }">
    <section class="panel">
      <div class="panel-header"><h3>{{ title }}</h3></div>
      <el-form class="business-fill-filter-form" :class="{ 'role-management-filter-form': isRole, 'upgrade-management-filter-form': isUpgrade }" :label-width="isRole ? undefined : '86px'">
        <el-form-item :label="isUpgrade ? '持卡人' : isRole ? '角色' : '员工'">
          <el-input v-model="filters.keyword" :placeholder="isUpgrade ? '持卡人姓名、手机号、卡号' : '请输入关键词'" clearable />
        </el-form-item>
        <el-form-item v-if="isUpgrade" label="卡片等级">
          <el-select v-model="filters.cardLevel" clearable placeholder="全部">
            <el-option label="全部" value="" />
            <el-option v-for="level in TALENT_CARD_LEVELS" :key="level" :label="level" :value="level" />
          </el-select>
        </el-form-item>
        <el-form-item label="状态">
          <el-select v-model="filters.status" clearable placeholder="全部">
            <el-option v-if="!isUpgrade" label="启用" value="1" />
            <el-option v-if="!isUpgrade" label="停用" value="0" />
            <el-option v-if="isUpgrade" label="全部" value="" />
            <el-option v-if="isUpgrade" label="待审核" value="待审核" />
            <el-option v-if="isUpgrade" label="已通过" value="已通过" />
            <el-option v-if="isUpgrade" label="已驳回" value="已驳回" />
          </el-select>
        </el-form-item>
        <el-form-item v-if="isUpgrade" label="申请时间" class="upgrade-management-filter-date">
          <el-date-picker v-model="filters.timeRange" type="daterange" value-format="YYYY-MM-DD" start-placeholder="开始日期" end-placeholder="结束日期" />
        </el-form-item>
        <div class="business-fill-filter-actions">
          <el-button type="primary" :icon="Search" @click="search">查询</el-button>
          <el-button :icon="Refresh" @click="reset">重置</el-button>
        </div>
      </el-form>
      <div class="business-fill-tools">
        <el-button v-if="!isUpgrade" v-permission="isRole ? 'admin.roles.create' : 'admin.staff.create'" type="primary" :icon="Plus" @click="openCreate">新建</el-button>
        <el-button v-permission="isUpgrade ? 'admin.upgrade-applications.export' : isRole ? 'admin.roles.export' : 'admin.staff.export'" :icon="Download" @click="download">下载</el-button>
      </div>
      <el-table v-loading="loading" :data="pagedRows" border class="business-fill-table" :empty-text="`暂无${title}数据`">
        <el-table-column v-for="[key, label] in columns" :key="key" :prop="key" :label="label" min-width="130" show-overflow-tooltip />
        <el-table-column label="操作" width="150" fixed="right" align="center">
          <template #default="{ row }">
            <el-button v-permission="isUpgrade ? 'admin.upgrade-applications.view' : isRole ? 'admin.roles.view' : 'admin.staff.view'" link type="primary" :icon="View" @click="view(row)">查看</el-button>
            <el-button v-if="!isUpgrade && !(isRole && isPlatformAdminRole(row))" v-permission="isRole ? 'admin.roles.edit' : 'admin.staff.edit'" link type="primary" :icon="Edit" @click="openEdit(row)">编辑</el-button>
            <template v-if="isRole && !isPlatformAdminRole(row)">
              <el-button v-permission="'admin.roles.disable'" link type="primary" @click="changeRoleStatus(row)">{{ Number(row.status) === 1 ? '停用' : '启用' }}</el-button>
              <el-button v-permission="'admin.roles.delete'" link type="danger" @click="removeRole(row)">删除</el-button>
            </template>
          </template>
        </el-table-column>
      </el-table>
      <div class="pagination-row">
        <el-pagination v-model:current-page="pagination.page" v-model:page-size="pagination.size" background layout="total, sizes, prev, pager, next" :page-sizes="[10, 20, 50]" :total="pageTotal" @change="changePage" />
      </div>
    </section>
    <el-dialog v-model="dialogVisible" :title="title" width="520px" :close-on-click-modal="false">
      <el-descriptions v-if="activeRow" :column="1" border>
        <el-descriptions-item v-for="[key, label] in columns" :key="key" :label="label">{{ activeRow[key] || '-' }}</el-descriptions-item>
      </el-descriptions>
    </el-dialog>
    <el-dialog v-model="editVisible" :title="`编辑${title}`" width="480px" destroy-on-close :close-on-click-modal="false">
      <el-form ref="formRef" :model="form" :rules="rules" label-width="92px">
        <el-form-item :label="isRole ? '角色名称' : '员工姓名'" prop="name" required><el-input v-model="form.name" /></el-form-item>
        <el-form-item :label="isRole ? '角色编码' : '登录账号'" prop="code" required><el-input v-model="form.code" :placeholder="isRole ? '' : '仅支持英文和数字'" maxlength="32" /></el-form-item>
        <el-form-item v-if="!isRole" label="登录密码" prop="password" :required="!editingId"><el-input v-model="form.password" type="password" :placeholder="editingId ? '留空则不修改' : '请输入登录密码'" /></el-form-item>
        <el-form-item v-if="isRole" label="备注"><el-input v-model="form.remark" /></el-form-item>
        <el-form-item label="状态"><el-switch v-model="form.status" :active-value="1" :inactive-value="0" /></el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="editVisible = false">取消</el-button>
        <el-button type="primary" @click="save">保存</el-button>
      </template>
    </el-dialog>
  </div>
</template>