index.vue 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. <template>
  2. <div class="user-bind-container">
  3. <ProTable
  4. ref="proTableRef"
  5. :columns="columns"
  6. :request-api="getUserBindList"
  7. :search-columns="searchColumns"
  8. :toolbar-buttons="toolbarButtons"
  9. :show-pagination="true"
  10. >
  11. <!-- 绑定状态列 -->
  12. <template #bindStatus="{ row }">
  13. <el-tag :type="getStatusType(row.bindStatus)">
  14. {{ getStatusText(row.bindStatus) }}
  15. </el-tag>
  16. </template>
  17. <!-- 绑定时间列 -->
  18. <template #bindTime="{ row }">
  19. {{ formatTime(row.bindTime) }}
  20. </template>
  21. <!-- 操作列 -->
  22. <template #actions="{ row }">
  23. <el-button link type="primary" @click="handleView(row)">查看</el-button>
  24. <el-button link type="primary" @click="handleEdit(row)">编辑</el-button>
  25. <el-button
  26. link
  27. type="danger"
  28. @click="handleDelete(row)"
  29. :disabled="row.bindStatus === 0"
  30. >
  31. 解绑
  32. </el-button>
  33. </template>
  34. </ProTable>
  35. <!-- 新增/编辑对话框 -->
  36. <el-dialog
  37. v-model="dialogVisible"
  38. :title="dialogTitle"
  39. width="600px"
  40. @close="handleDialogClose"
  41. >
  42. <el-form
  43. ref="formRef"
  44. :model="formData"
  45. :rules="formRules"
  46. label-width="140px"
  47. >
  48. <el-form-item label="平台用户ID" prop="userId">
  49. <el-input
  50. v-model.number="formData.userId"
  51. placeholder="请输入平台用户ID"
  52. />
  53. </el-form-item>
  54. <el-form-item label="Telegram ID" prop="telegramId">
  55. <el-input
  56. v-model.number="formData.telegramId"
  57. placeholder="请输入Telegram用户ID(可选)"
  58. />
  59. </el-form-item>
  60. <el-form-item label="Telegram用户名" prop="telegramUsername">
  61. <el-input
  62. v-model="formData.telegramUsername"
  63. placeholder="请输入Telegram用户名(不含@)"
  64. />
  65. </el-form-item>
  66. <el-form-item label="Telegram名字">
  67. <el-input
  68. v-model="formData.telegramFirstName"
  69. placeholder="请输入Telegram名字(可选)"
  70. />
  71. </el-form-item>
  72. <el-form-item label="绑定状态" v-if="isEdit">
  73. <el-select v-model="formData.bindStatus" placeholder="请选择状态">
  74. <el-option label="已绑定" :value="1" />
  75. <el-option label="已解绑" :value="0" />
  76. </el-select>
  77. </el-form-item>
  78. </el-form>
  79. <template #footer>
  80. <el-button @click="dialogVisible = false">取消</el-button>
  81. <el-button type="primary" @click="handleSubmit">确定</el-button>
  82. </template>
  83. </el-dialog>
  84. <!-- 查看对话框 -->
  85. <el-dialog v-model="viewDialogVisible" title="绑定详情" width="600px">
  86. <el-descriptions :column="2" border>
  87. <el-descriptions-item label="ID">
  88. {{ viewData.id }}
  89. </el-descriptions-item>
  90. <el-descriptions-item label="平台用户ID">
  91. {{ viewData.userId }}
  92. </el-descriptions-item>
  93. <el-descriptions-item label="Telegram ID">
  94. {{ viewData.telegramId || '-' }}
  95. </el-descriptions-item>
  96. <el-descriptions-item label="Telegram用户名">
  97. {{ viewData.telegramUsername || '-' }}
  98. </el-descriptions-item>
  99. <el-descriptions-item label="Telegram名字">
  100. {{ viewData.telegramFirstName || '-' }}
  101. </el-descriptions-item>
  102. <el-descriptions-item label="绑定状态">
  103. <el-tag :type="getStatusType(viewData.bindStatus)">
  104. {{ getStatusText(viewData.bindStatus) }}
  105. </el-tag>
  106. </el-descriptions-item>
  107. <el-descriptions-item label="绑定时间" :span="2">
  108. {{ formatTime(viewData.bindTime) }}
  109. </el-descriptions-item>
  110. <el-descriptions-item label="创建时间" :span="2">
  111. {{ formatTimestamp(viewData.createdAt) }}
  112. </el-descriptions-item>
  113. <el-descriptions-item label="更新时间" :span="2">
  114. {{ formatTimestamp(viewData.updatedAt) }}
  115. </el-descriptions-item>
  116. </el-descriptions>
  117. </el-dialog>
  118. </div>
  119. </template>
  120. <script setup>
  121. import { ref, reactive } from 'vue';
  122. import { ElMessage, ElMessageBox } from 'element-plus';
  123. import ProTable from '@/components/ProTable/index.vue';
  124. import {
  125. getUserBindList,
  126. createUserBind,
  127. updateUserBind,
  128. deleteUserBind
  129. } from '@/api/modules/redpacket';
  130. const proTableRef = ref();
  131. const formRef = ref();
  132. const dialogVisible = ref(false);
  133. const viewDialogVisible = ref(false);
  134. const dialogTitle = ref('');
  135. const isEdit = ref(false);
  136. const formData = reactive({
  137. id: null,
  138. userId: null,
  139. telegramId: null,
  140. telegramUsername: '',
  141. telegramFirstName: '',
  142. bindStatus: 1
  143. });
  144. const viewData = ref({});
  145. // 表格列配置
  146. const columns = [
  147. { prop: 'id', label: 'ID', width: 80 },
  148. { prop: 'userId', label: '平台用户ID', width: 120 },
  149. { prop: 'telegramId', label: 'Telegram ID', width: 150 },
  150. { prop: 'telegramUsername', label: 'Telegram用户名', minWidth: 150 },
  151. { prop: 'telegramFirstName', label: 'Telegram名字', width: 150 },
  152. { prop: 'bindStatus', label: '绑定状态', width: 100, slot: 'bindStatus' },
  153. { prop: 'bindTime', label: '绑定时间', width: 180, slot: 'bindTime' }
  154. ];
  155. // 搜索列配置
  156. const searchColumns = [
  157. { prop: 'user_id', label: '平台用户ID', type: 'input' },
  158. { prop: 'telegram_username', label: 'Telegram用户名', type: 'input' },
  159. {
  160. prop: 'bind_status',
  161. label: '绑定状态',
  162. type: 'select',
  163. options: [
  164. { label: '已绑定', value: 1 },
  165. { label: '已解绑', value: 0 }
  166. ]
  167. }
  168. ];
  169. // 工具栏按钮
  170. const toolbarButtons = [
  171. {
  172. label: '新增绑定',
  173. type: 'primary',
  174. icon: 'Plus',
  175. onClick: handleAdd
  176. }
  177. ];
  178. // 表单验证规则
  179. const formRules = {
  180. userId: [{ required: true, message: '请输入平台用户ID', trigger: 'blur' }],
  181. telegramUsername: [{ required: true, message: '请输入Telegram用户名', trigger: 'blur' }]
  182. };
  183. // 获取状态文本
  184. const getStatusText = status => {
  185. const statusMap = {
  186. 1: '已绑定',
  187. 0: '已解绑'
  188. };
  189. return statusMap[status] !== undefined ? statusMap[status] : '未知';
  190. };
  191. // 获取状态类型
  192. const getStatusType = status => {
  193. const typeMap = {
  194. 1: 'success',
  195. 0: 'info'
  196. };
  197. return typeMap[status] || '';
  198. };
  199. // 格式化时间戳(秒)
  200. const formatTimestamp = timestamp => {
  201. if (!timestamp) return '-';
  202. const date = new Date(timestamp * 1000);
  203. return date.toLocaleString('zh-CN');
  204. };
  205. // 格式化时间(ISO 字符串或时间戳)
  206. const formatTime = time => {
  207. if (!time) return '-';
  208. const date = new Date(time);
  209. if (isNaN(date.getTime())) return '-';
  210. return date.toLocaleString('zh-CN');
  211. };
  212. // 新增
  213. function handleAdd() {
  214. dialogTitle.value = '新增绑定';
  215. isEdit.value = false;
  216. resetForm();
  217. dialogVisible.value = true;
  218. }
  219. // 编辑
  220. function handleEdit(row) {
  221. dialogTitle.value = '编辑绑定';
  222. isEdit.value = true;
  223. Object.assign(formData, {
  224. id: row.id,
  225. userId: row.userId,
  226. telegramId: row.telegramId,
  227. telegramUsername: row.telegramUsername,
  228. telegramFirstName: row.telegramFirstName,
  229. bindStatus: row.bindStatus
  230. });
  231. dialogVisible.value = true;
  232. }
  233. // 查看
  234. function handleView(row) {
  235. viewData.value = { ...row };
  236. viewDialogVisible.value = true;
  237. }
  238. // 解绑
  239. async function handleDelete(row) {
  240. try {
  241. await ElMessageBox.confirm(
  242. `确定要解绑用户 "${row.telegramUsername}" 吗?此操作会将绑定状态改为已解绑。`,
  243. '提示',
  244. {
  245. confirmButtonText: '确定',
  246. cancelButtonText: '取消',
  247. type: 'warning'
  248. }
  249. );
  250. await deleteUserBind(row.id);
  251. ElMessage.success('解绑成功');
  252. proTableRef.value.refresh();
  253. } catch (error) {
  254. if (error !== 'cancel') {
  255. ElMessage.error(error.message || '解绑失败');
  256. }
  257. }
  258. }
  259. // 提交表单
  260. async function handleSubmit() {
  261. try {
  262. await formRef.value.validate();
  263. const api = isEdit.value ? updateUserBind : createUserBind;
  264. await api(formData);
  265. ElMessage.success(isEdit.value ? '更新成功' : '创建成功');
  266. dialogVisible.value = false;
  267. proTableRef.value.refresh();
  268. } catch (error) {
  269. if (error !== false) {
  270. ElMessage.error(error.message || '操作失败');
  271. }
  272. }
  273. }
  274. // 重置表单
  275. function resetForm() {
  276. Object.assign(formData, {
  277. id: null,
  278. userId: null,
  279. telegramId: null,
  280. telegramUsername: '',
  281. telegramFirstName: '',
  282. bindStatus: 1
  283. });
  284. formRef.value?.clearValidate();
  285. }
  286. // 关闭对话框
  287. function handleDialogClose() {
  288. resetForm();
  289. }
  290. </script>
  291. <style scoped lang="scss">
  292. .user-bind-container {
  293. padding: 20px;
  294. }
  295. </style>