index.vue 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. <template>
  2. <div class="sub-account-management-container">
  3. <ProTable
  4. ref="proTable"
  5. :columns="columns"
  6. :request-api="getTableList"
  7. :init-param="initParam"
  8. :data-callback="dataCallback"
  9. row-key="userId"
  10. >
  11. <!-- 表格 header 按钮 -->
  12. <template #tableHeader="scope">
  13. <div class="table-header-btn">
  14. <el-button type="primary" :icon="Plus" @click="handleCreateSubAccount"> 新建账号 </el-button>
  15. <el-button :icon="Delete" :disabled="!scope.isSelected" @click="handleBatchDelete"> 批量删除 </el-button>
  16. </div>
  17. </template>
  18. <template #tableHeaderRight>
  19. <el-link type="primary" :underline="false"> 历史操作记录 </el-link>
  20. </template>
  21. <!-- 可操作权限列 -->
  22. <template #permissionCount="scope">
  23. <span>{{ scope.row.permissionCount || 0 }}个</span>
  24. </template>
  25. <!-- 操作列 -->
  26. <template #operation="scope">
  27. <el-button link type="primary" @click="handleView(scope.row)"> 查看 </el-button>
  28. <el-button link type="primary" @click="handleEdit(scope.row)"> 编辑 </el-button>
  29. <el-button link type="danger" @click="handleDelete(scope.row)"> 删除 </el-button>
  30. </template>
  31. </ProTable>
  32. <!-- 删除确认对话框 -->
  33. <el-dialog v-model="deleteDialogVisible" title="删除确认" width="400px" :close-on-click-modal="false">
  34. <div class="delete-content">
  35. <p v-if="deleteType === 'single'">确定要删除子账号"{{ currentAccount?.accountName }}"吗?</p>
  36. <p v-else>确定要删除选中的 {{ selectedAccounts.length }} 个子账号吗?</p>
  37. <p class="delete-warning">此操作不可恢复!</p>
  38. </div>
  39. <template #footer>
  40. <div class="dialog-footer">
  41. <el-button @click="deleteDialogVisible = false"> 取消 </el-button>
  42. <el-button type="danger" @click="handleConfirmDelete" :loading="deleteLoading"> 确定删除 </el-button>
  43. </div>
  44. </template>
  45. </el-dialog>
  46. </div>
  47. </template>
  48. <script setup lang="tsx" name="subAccountManagement">
  49. import { ref, reactive, onMounted, computed } from "vue";
  50. import { useRouter } from "vue-router";
  51. import { ElMessage } from "element-plus";
  52. import { Plus, Delete } from "@element-plus/icons-vue";
  53. import ProTable from "@/components/ProTable/index.vue";
  54. import { ColumnProps, ProTableInstance } from "@/components/ProTable/interface";
  55. import { querySubAccounts, getAllNormalRoles, type RoleItem } from "@/api/modules/accountRoleManagement";
  56. import { localGet } from "@/utils";
  57. // 子账号数据类型
  58. interface SubAccountItem {
  59. userId: number;
  60. accountName: string;
  61. phone: string;
  62. roleId: number;
  63. roleName: string;
  64. permissionCount: number;
  65. }
  66. // 路由
  67. const router = useRouter();
  68. // ProTable 实例
  69. const proTable = ref<ProTableInstance>();
  70. // 角色列表(用于搜索下拉框)
  71. const roleList = ref<RoleItem[]>([]);
  72. // 选中的账号
  73. const selectedAccounts = ref<SubAccountItem[]>([]);
  74. // 删除相关
  75. const deleteDialogVisible = ref(false);
  76. const deleteLoading = ref(false);
  77. const deleteType = ref<"single" | "batch">("single");
  78. const currentAccount = ref<SubAccountItem | null>(null);
  79. // 获取 storeId
  80. const getStoreId = (): string => {
  81. return localGet("createdId") || localGet("geeker-user")?.userInfo?.storeId || "";
  82. };
  83. // 加载角色列表(用于搜索下拉框)
  84. const loadRoleList = async () => {
  85. try {
  86. const storeId = getStoreId();
  87. if (!storeId) return;
  88. const res = await getAllNormalRoles({ storeId: Number(storeId) });
  89. const code = typeof res.code === "string" ? parseInt(res.code) : res.code;
  90. if (code === 200) {
  91. roleList.value = (res.data || []).map((role: any) => ({
  92. ...role,
  93. id: role.roleId || role.id,
  94. roleId: role.roleId || role.id
  95. }));
  96. }
  97. } catch (error) {
  98. console.error("获取角色列表失败:", error);
  99. }
  100. };
  101. // 表格列配置
  102. const columns = reactive<ColumnProps<SubAccountItem>[]>([
  103. {
  104. type: "selection",
  105. fixed: "left",
  106. width: 55
  107. },
  108. {
  109. prop: "accountName",
  110. label: "账号名称",
  111. search: {
  112. el: "input",
  113. props: { placeholder: "请输入账号名称" }
  114. }
  115. },
  116. {
  117. prop: "phone",
  118. label: "手机号",
  119. search: {
  120. el: "input",
  121. props: { placeholder: "请输入手机号" }
  122. }
  123. },
  124. {
  125. prop: "roleName",
  126. label: "角色",
  127. search: {
  128. el: "select",
  129. props: { placeholder: "请选择角色", clearable: true },
  130. key: "roleName"
  131. },
  132. enum: computed(() => {
  133. return roleList.value.map(role => ({
  134. label: role.roleName,
  135. value: role.roleName
  136. }));
  137. }),
  138. fieldNames: { label: "label", value: "value" }
  139. },
  140. {
  141. prop: "permissionCount",
  142. label: "可操作权限"
  143. },
  144. {
  145. prop: "operation",
  146. label: "操作",
  147. fixed: "right",
  148. width: 180
  149. }
  150. ]);
  151. // 初始化参数
  152. const initParam = reactive({
  153. storeId: getStoreId()
  154. });
  155. // 数据回调处理
  156. const dataCallback = (data: any) => {
  157. // 如果返回的是数组,转换为分页格式
  158. if (Array.isArray(data)) {
  159. return {
  160. list: data,
  161. total: data.length
  162. };
  163. }
  164. // 如果返回的是分页对象
  165. if (data.records) {
  166. return {
  167. list: data.records || [],
  168. total: data.total || 0
  169. };
  170. }
  171. // 默认返回
  172. return {
  173. list: [],
  174. total: 0
  175. };
  176. };
  177. // 请求接口
  178. const getTableList = (params: any) => {
  179. const storeId = getStoreId();
  180. if (!storeId) {
  181. ElMessage.warning("缺少店铺ID");
  182. return Promise.resolve({ code: 200, data: [], msg: "缺少店铺ID" });
  183. }
  184. return querySubAccounts(Number(storeId), params.accountName || "", params.phone || "", params.roleName || "");
  185. };
  186. // 创建子账号
  187. const handleCreateSubAccount = () => {
  188. router.push("/accountRoleManagement/subAccountManagement/create");
  189. };
  190. // 查看
  191. const handleView = (row: SubAccountItem) => {
  192. // TODO: 实现查看功能
  193. ElMessage.info("查看功能待实现");
  194. };
  195. // 编辑
  196. const handleEdit = (row: SubAccountItem) => {
  197. router.push({
  198. path: "/accountRoleManagement/subAccountManagement/create",
  199. query: {
  200. id: row.userId.toString(),
  201. accountName: row.accountName
  202. }
  203. });
  204. };
  205. // 删除单个账号
  206. const handleDelete = (row: SubAccountItem) => {
  207. currentAccount.value = row;
  208. deleteType.value = "single";
  209. deleteDialogVisible.value = true;
  210. };
  211. // 批量删除
  212. const handleBatchDelete = () => {
  213. const selectedList = proTable.value?.selectedList || [];
  214. if (selectedList.length === 0) {
  215. ElMessage.warning("请选择要删除的子账号");
  216. return;
  217. }
  218. selectedAccounts.value = selectedList as SubAccountItem[];
  219. deleteType.value = "batch";
  220. deleteDialogVisible.value = true;
  221. };
  222. // 确认删除
  223. const handleConfirmDelete = async () => {
  224. deleteLoading.value = true;
  225. try {
  226. // TODO: 调用删除接口
  227. // 模拟接口调用
  228. await new Promise(resolve => setTimeout(resolve, 500));
  229. if (deleteType.value === "single" && currentAccount.value) {
  230. // 单个删除
  231. ElMessage.success("删除成功");
  232. } else {
  233. // 批量删除
  234. ElMessage.success(`成功删除 ${selectedAccounts.value.length} 个子账号`);
  235. }
  236. // 刷新表格
  237. proTable.value?.getTableList();
  238. deleteDialogVisible.value = false;
  239. currentAccount.value = null;
  240. selectedAccounts.value = [];
  241. } catch (error) {
  242. ElMessage.error("删除失败,请重试");
  243. } finally {
  244. deleteLoading.value = false;
  245. }
  246. };
  247. // 初始化
  248. onMounted(() => {
  249. loadRoleList();
  250. });
  251. </script>
  252. <style lang="scss" scoped>
  253. .sub-account-management-container {
  254. min-height: calc(100vh - 84px);
  255. padding: 20px;
  256. background: #ffffff;
  257. }
  258. .table-header-btn {
  259. display: flex;
  260. gap: 12px;
  261. align-items: center;
  262. }
  263. // 删除对话框内容
  264. .delete-content {
  265. p {
  266. margin: 0 0 12px;
  267. font-size: 14px;
  268. color: var(--el-text-color-primary);
  269. &:last-child {
  270. margin-bottom: 0;
  271. }
  272. }
  273. .delete-warning {
  274. font-weight: 500;
  275. color: #e6a23c;
  276. }
  277. }
  278. // 对话框底部
  279. .dialog-footer {
  280. display: flex;
  281. gap: 12px;
  282. justify-content: flex-end;
  283. }
  284. </style>