index.vue 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. <template>
  2. <div class="app-container h-full flex flex-1 flex-col">
  3. <!-- 搜索 -->
  4. <page-search
  5. ref="searchRef"
  6. :search-config="searchConfig"
  7. @query-click="handleQueryClick"
  8. @reset-click="handleResetClick"
  9. ></page-search>
  10. <!-- 列表 -->
  11. <page-content
  12. ref="contentRef"
  13. :content-config="contentConfig"
  14. @add-click="handleAddClick"
  15. @export-click="handleExportClick"
  16. @search-click="handleSearchClick"
  17. @toolbar-click="handleToolbarClick"
  18. @operate-click="handleOperateClick"
  19. @filter-change="handleFilterChange"
  20. @sort-change="handleSortChange"
  21. >
  22. <template #balance="scope">
  23. <span class="money-text primary">¥ {{ formatMoney(scope.row.balance) }}</span>
  24. </template>
  25. <template #totalConsumption="scope">
  26. <span class="money-text warning">¥ {{ formatMoney(scope.row.totalConsumption) }}</span>
  27. </template>
  28. </page-content>
  29. <!-- 详情 -->
  30. <page-modal
  31. ref="editModalRef"
  32. :modal-config="editModalConfig"
  33. @submit-click="handleSubmitClick"
  34. ></page-modal>
  35. </div>
  36. </template>
  37. <script setup lang="ts">
  38. defineOptions({ name: "UserInfo" });
  39. import UserInfoAPI, { UserInfoForm, UserInfoPageQuery } from "@/api/userManage/user-info-api";
  40. import UserFirmAPI from "@/api/toBManage/user-firm-api";
  41. import type { IObject, IModalConfig, IContentConfig, ISearchConfig } from "@/components/CURD/types";
  42. import usePage from "@/components/CURD/usePage";
  43. // 组合式 CRUD
  44. const {
  45. searchRef,
  46. contentRef,
  47. editModalRef,
  48. handleQueryClick,
  49. handleResetClick,
  50. handleAddClick,
  51. handleEditClick,
  52. handleSubmitClick,
  53. handleExportClick,
  54. handleSearchClick,
  55. handleFilterChange,
  56. } = usePage();
  57. // 金额格式化函数
  58. const formatMoney = (value: number | undefined | null): string => {
  59. if (value === null || value === undefined) return "0.00";
  60. return Math.abs(value).toLocaleString("zh-CN", {
  61. minimumFractionDigits: 2,
  62. maximumFractionDigits: 2,
  63. });
  64. };
  65. // 搜索配置
  66. const searchConfig: ISearchConfig = reactive({
  67. permPrefix: "business:user-info",
  68. formItems: [
  69. {
  70. type: "input",
  71. label: "手机号",
  72. prop: "phone",
  73. attrs: {
  74. placeholder: "手机号",
  75. clearable: true,
  76. style: { width: "200px" },
  77. },
  78. },
  79. {
  80. type: "select",
  81. label: "所属企业",
  82. prop: "firmId",
  83. attrs: {
  84. placeholder: "请选择企业",
  85. clearable: true,
  86. filterable: true,
  87. style: { width: "200px" },
  88. },
  89. options: [],
  90. async initFn(formItem) {
  91. try {
  92. const response = await UserFirmAPI.getFirmList();
  93. if (Array.isArray(response)) {
  94. formItem.options = response.map((item: any) => ({
  95. label: item.name,
  96. value: item.id,
  97. }));
  98. }
  99. } catch (error) {
  100. console.error("Failed to fetch firm list:", error);
  101. }
  102. },
  103. },
  104. ],
  105. });
  106. // 列表配置
  107. const contentConfig: IContentConfig<UserInfoPageQuery> = reactive({
  108. // 权限前缀
  109. permPrefix: "business:user-info",
  110. table: {
  111. border: true,
  112. highlightCurrentRow: true,
  113. },
  114. // 主键
  115. pk: "id",
  116. // 列表查询接口
  117. indexAction: UserInfoAPI.getPage,
  118. // 数据解析函数
  119. parseData(res: any) {
  120. return {
  121. total: res.total,
  122. list: res.list,
  123. };
  124. },
  125. // 分页配置
  126. pagination: {
  127. background: true,
  128. layout: "total, sizes, prev, pager, next, jumper",
  129. pageSize: 20,
  130. pageSizes: [10, 20, 30, 50],
  131. },
  132. // 工具栏配置
  133. defaultToolbar: ["refresh", "filter"],
  134. // 表格列配置
  135. cols: [
  136. { type: "selection", width: 50, align: "center" },
  137. { label: "用户ID", prop: "id", width: 100 },
  138. { label: "手机号", prop: "phone" },
  139. { label: "昵称", prop: "nickName" },
  140. { label: "所属企业", prop: "firmName" },
  141. {
  142. label: "当前余额",
  143. prop: "balance",
  144. sortable: "custom",
  145. templet: "custom",
  146. slotName: "balance",
  147. },
  148. {
  149. label: "累计消费",
  150. prop: "totalConsumption",
  151. sortable: "custom",
  152. templet: "custom",
  153. slotName: "totalConsumption",
  154. },
  155. { label: "创建时间", prop: "createTime" },
  156. {
  157. label: "操作",
  158. prop: "operation",
  159. width: 100,
  160. templet: "tool",
  161. operat: [
  162. {
  163. name: "detail",
  164. text: "详情",
  165. attrs: {
  166. type: "primary",
  167. icon: "view",
  168. link: true,
  169. size: "small",
  170. },
  171. },
  172. ],
  173. },
  174. ],
  175. });
  176. // 详情配置
  177. const editModalConfig: IModalConfig<UserInfoForm> = reactive({
  178. permPrefix: "business:user-info",
  179. component: "drawer",
  180. drawer: {
  181. title: "用户详情",
  182. size: 500,
  183. },
  184. pk: "id",
  185. formItems: [
  186. {
  187. type: "input",
  188. attrs: {
  189. placeholder: "用户ID",
  190. disabled: true,
  191. },
  192. label: "用户ID",
  193. prop: "id",
  194. },
  195. {
  196. type: "input",
  197. attrs: {
  198. placeholder: "手机号",
  199. disabled: true,
  200. },
  201. label: "手机号",
  202. prop: "phone",
  203. },
  204. {
  205. type: "input",
  206. attrs: {
  207. placeholder: "昵称",
  208. disabled: true,
  209. },
  210. label: "昵称",
  211. prop: "nickName",
  212. },
  213. {
  214. type: "input",
  215. attrs: {
  216. placeholder: "微信openid",
  217. disabled: true,
  218. },
  219. label: "微信openid",
  220. prop: "openid",
  221. },
  222. ],
  223. });
  224. // 处理操作按钮点击
  225. const handleOperateClick = (data: IObject) => {
  226. if (data.name === "detail") {
  227. handleEditClick(data.row, async () => {
  228. return await UserInfoAPI.getFormData(data.row.id);
  229. });
  230. }
  231. };
  232. // 处理工具栏按钮点击
  233. const handleToolbarClick = (name: string) => {
  234. console.log(name);
  235. };
  236. // 排序参数
  237. const sortParams = reactive({
  238. sortField: "",
  239. sortOrder: "",
  240. });
  241. // 处理排序变化
  242. const handleSortChange = (data: { prop: string; order: string | null }) => {
  243. sortParams.sortField = data.prop || "";
  244. sortParams.sortOrder = data.order || "";
  245. // 带着排序参数重新查询
  246. contentRef.value?.fetchPageData({ ...sortParams });
  247. };
  248. </script>
  249. <style scoped>
  250. .money-text {
  251. font-weight: bold;
  252. font-size: 13px;
  253. }
  254. .money-text.primary {
  255. color: #409eff;
  256. }
  257. .money-text.success {
  258. color: #67c23a;
  259. }
  260. .money-text.danger {
  261. color: #f56c6c;
  262. }
  263. .money-text.warning {
  264. color: #e6a23c;
  265. }
  266. </style>