@befly-addon/admin 1.0.52 → 1.0.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,181 @@
1
+ <template>
2
+ <div class="page-api page-table">
3
+ <div class="main-tool">
4
+ <div class="left">
5
+ <TInput v-model="$Data.searchKeyword" placeholder="搜索接口名称或路径" clearable style="width: 300px" @enter="$Method.handleSearch" @clear="$Method.handleSearch">
6
+ <template #suffix-icon>
7
+ <ILucideSearch />
8
+ </template>
9
+ </TInput>
10
+ <span style="margin-left: 16px; color: var(--text-secondary); font-size: 13px">共 {{ $Data.allData.length }} 个接口</span>
11
+ </div>
12
+ <div class="right">
13
+ <TButton shape="circle" @click="$Method.handleRefresh">
14
+ <template #icon>
15
+ <ILucideRotateCw />
16
+ </template>
17
+ </TButton>
18
+ </div>
19
+ </div>
20
+
21
+ <div class="main-content">
22
+ <div class="main-table">
23
+ <TTable v-bind="withTableProps()" :data="$Data.tableData" :columns="$Data.columns" :loading="$Data.loading" :selected-row-keys="$Data.selectedRowKeys" :active-row-keys="$Data.activeRowKeys" @select-change="$Method.onSelectChange" @active-change="$Method.onActiveChange">
24
+ <template #method="{ row }">
25
+ <TTag v-if="row.method === 'GET'" shape="round" theme="success" variant="light-outline">GET</TTag>
26
+ <TTag v-else-if="row.method === 'POST'" shape="round" theme="primary" variant="light-outline">POST</TTag>
27
+ <TTag v-else-if="row.method === 'PUT'" shape="round" theme="warning" variant="light-outline">PUT</TTag>
28
+ <TTag v-else-if="row.method === 'DELETE'" shape="round" theme="danger" variant="light-outline">DELETE</TTag>
29
+ <TTag v-else shape="round" variant="light-outline">{{ row.method }}</TTag>
30
+ </template>
31
+ <template #addonName="{ row }">
32
+ <TTag v-if="row.addonName" shape="round" variant="light-outline">{{ row.addonTitle || row.addonName }}</TTag>
33
+ <span v-else>项目</span>
34
+ </template>
35
+ </TTable>
36
+ </div>
37
+
38
+ <div class="main-detail">
39
+ <DetailPanel
40
+ :data="$Data.currentRow"
41
+ :fields="[
42
+ { key: 'id', label: 'ID' },
43
+ { key: 'name', label: '接口名称' },
44
+ { key: 'path', label: '接口路径' },
45
+ { key: 'method', label: '请求方法' },
46
+ { key: 'addonName', label: '所属组件', default: '项目' },
47
+ { key: 'description', label: '接口描述' }
48
+ ]"
49
+ >
50
+ <template #method="{ value }">
51
+ <TTag v-if="value === 'GET'" shape="round" theme="success" variant="light-outline">GET</TTag>
52
+ <TTag v-else-if="value === 'POST'" shape="round" theme="primary" variant="light-outline">POST</TTag>
53
+ <TTag v-else-if="value === 'PUT'" shape="round" theme="warning" variant="light-outline">PUT</TTag>
54
+ <TTag v-else-if="value === 'DELETE'" shape="round" theme="danger" variant="light-outline">DELETE</TTag>
55
+ <TTag v-else shape="round" variant="light-outline">{{ value }}</TTag>
56
+ </template>
57
+ </DetailPanel>
58
+ </div>
59
+ </div>
60
+ </div>
61
+ </template>
62
+
63
+ <script setup>
64
+ import { Button as TButton, Table as TTable, Tag as TTag, Input as TInput, MessagePlugin } from 'tdesign-vue-next';
65
+ import ILucideRotateCw from '~icons/lucide/rotate-cw';
66
+ import ILucideSearch from '~icons/lucide/search';
67
+ import { $Http } from '@/plugins/http';
68
+ import { withDefaultColumns, withTableProps } from '@/utils';
69
+ import DetailPanel from '@/components/DetailPanel.vue';
70
+
71
+ // 响应式数据
72
+ const $Data = $ref({
73
+ tableData: [],
74
+ allData: [],
75
+ loading: false,
76
+ searchKeyword: '',
77
+ columns: withDefaultColumns([
78
+ {
79
+ colKey: 'row-select',
80
+ type: 'single',
81
+ width: 50,
82
+ fixed: 'left',
83
+ checkProps: { allowUncheck: true },
84
+ ellipsis: false
85
+ },
86
+ { colKey: 'name', title: '接口名称', width: 200, fixed: 'left' },
87
+ { colKey: 'id', title: '序号', width: 80, align: 'center' },
88
+ { colKey: 'path', title: '接口路径', width: 350 },
89
+ { colKey: 'method', title: '请求方法', width: 100, align: 'center', ellipsis: false },
90
+ { colKey: 'addonName', title: '所属组件', width: 120, ellipsis: false }
91
+ ]),
92
+ currentRow: null,
93
+ selectedRowKeys: [],
94
+ activeRowKeys: []
95
+ });
96
+
97
+ // 方法
98
+ const $Method = {
99
+ async initData() {
100
+ await $Method.loadApiAll();
101
+ },
102
+
103
+ // 加载全部接口
104
+ async loadApiAll() {
105
+ $Data.loading = true;
106
+ try {
107
+ const res = await $Http('/addon/admin/api/all');
108
+ const list = res.data?.lists || [];
109
+ $Data.allData = list;
110
+ $Data.tableData = list;
111
+
112
+ // 自动选中并高亮第一行
113
+ if ($Data.tableData.length > 0) {
114
+ $Data.currentRow = $Data.tableData[0];
115
+ $Data.selectedRowKeys = [$Data.tableData[0].id];
116
+ $Data.activeRowKeys = [$Data.tableData[0].id];
117
+ } else {
118
+ $Data.currentRow = null;
119
+ $Data.selectedRowKeys = [];
120
+ $Data.activeRowKeys = [];
121
+ }
122
+ } catch (error) {
123
+ console.error('加载接口列表失败:', error);
124
+ MessagePlugin.error('加载数据失败');
125
+ } finally {
126
+ $Data.loading = false;
127
+ }
128
+ },
129
+
130
+ // 刷新
131
+ handleRefresh() {
132
+ $Method.loadApiAll();
133
+ },
134
+
135
+ // 搜索
136
+ handleSearch() {
137
+ if (!$Data.searchKeyword) {
138
+ $Data.tableData = $Data.allData;
139
+ return;
140
+ }
141
+ const keyword = $Data.searchKeyword.toLowerCase();
142
+ $Data.tableData = $Data.allData.filter((item) => item.name?.toLowerCase().includes(keyword) || item.path?.toLowerCase().includes(keyword));
143
+ },
144
+
145
+ // 单选变化
146
+ onSelectChange(value, { selectedRowData }) {
147
+ $Data.selectedRowKeys = value;
148
+ $Data.activeRowKeys = value;
149
+ if (selectedRowData && selectedRowData.length > 0) {
150
+ $Data.currentRow = selectedRowData[0];
151
+ } else if ($Data.tableData.length > 0) {
152
+ $Data.currentRow = $Data.tableData[0];
153
+ $Data.selectedRowKeys = [$Data.tableData[0].id];
154
+ $Data.activeRowKeys = [$Data.tableData[0].id];
155
+ } else {
156
+ $Data.currentRow = null;
157
+ }
158
+ },
159
+
160
+ // 高亮行变化
161
+ onActiveChange(value, { activeRowData }) {
162
+ $Data.activeRowKeys = value;
163
+ $Data.selectedRowKeys = value;
164
+ if (activeRowData && activeRowData.length > 0) {
165
+ $Data.currentRow = activeRowData[0];
166
+ } else if ($Data.tableData.length > 0) {
167
+ $Data.currentRow = $Data.tableData[0];
168
+ $Data.selectedRowKeys = [$Data.tableData[0].id];
169
+ $Data.activeRowKeys = [$Data.tableData[0].id];
170
+ } else {
171
+ $Data.currentRow = null;
172
+ }
173
+ }
174
+ };
175
+
176
+ $Method.initData();
177
+ </script>
178
+
179
+ <style scoped lang="scss">
180
+ // 样式继承自全局 page-table
181
+ </style>
@@ -28,7 +28,7 @@
28
28
  </TForm>
29
29
  <template #footer>
30
30
  <TButton @click="$Method.onClose">取消</TButton>
31
- <TButton theme="primary" @click="$Method.onSubmit">确定</TButton>
31
+ <TButton theme="primary" :loading="$Data.submitting" @click="$Method.onSubmit">确定</TButton>
32
32
  </template>
33
33
  </TDialog>
34
34
  </template>
@@ -62,6 +62,7 @@ const $From = $shallowRef({
62
62
 
63
63
  const $Data = $ref({
64
64
  visible: false,
65
+ submitting: false,
65
66
  formData: {
66
67
  id: 0,
67
68
  name: '',
@@ -127,16 +128,17 @@ const $Method = {
127
128
  const valid = await $From.form.validate();
128
129
  if (!valid) return;
129
130
 
131
+ $Data.submitting = true;
130
132
  const res = await $Http($Prop.actionType === 'add' ? '/addon/admin/dictIns' : '/addon/admin/dictUpd', $Data.formData);
131
133
 
132
- MessagePlugin.info({
133
- message: $Prop.actionType === 'add' ? '添加成功' : '编辑成功',
134
- status: 'success'
135
- });
134
+ MessagePlugin.success($Prop.actionType === 'add' ? '添加成功' : '编辑成功');
136
135
  $Method.onClose();
137
136
  $Emit('success');
138
137
  } catch (error) {
139
138
  console.error('提交失败:', error);
139
+ MessagePlugin.error('提交失败');
140
+ } finally {
141
+ $Data.submitting = false;
140
142
  }
141
143
  }
142
144
  };
@@ -16,33 +16,51 @@
16
16
  </TButton>
17
17
  </div>
18
18
  </div>
19
- <div class="main-table">
20
- <TTable :data="$Data.dictList" :columns="$Data.columns" header-cell-class-name="custom-table-cell-class" size="small" height="100%" row-key="id">
21
- <template #state="{ row }">
22
- <TTag v-if="row.state === 1" theme="success">正常</TTag>
23
- <TTag v-else-if="row.state === 2" theme="warning">禁用</TTag>
24
- <TTag v-else theme="danger">已删除</TTag>
25
- </template>
26
- <template #operation="{ row }">
27
- <TDropdown trigger="click" min-column-width="120" @click="(data) => $Method.onAction(data.value, row)">
28
- <TButton variant="text" size="small">操作</TButton>
29
- <TDropdownMenu slot="dropdown">
30
- <TDropdownItem value="upd">
31
- <ILucidePencil />
32
- 编辑
33
- </TDropdownItem>
34
- <TDropdownItem value="del" :divider="true">
35
- <ILucideTrash2 style="width: 14px; height: 14px; margin-right: 6px" />
36
- 删除
37
- </TDropdownItem>
38
- </TDropdownMenu>
39
- </TDropdown>
40
- </template>
41
- </TTable>
19
+ <div class="main-content">
20
+ <div class="main-table">
21
+ <TTable v-bind="withTableProps()" :data="$Data.dictList" :columns="$Data.columns" :loading="$Data.loading" :active-row-keys="$Data.activeRowKeys" @active-change="$Method.onActiveChange">
22
+ <template #state="{ row }">
23
+ <TTag v-if="row.state === 1" theme="success">正常</TTag>
24
+ <TTag v-else-if="row.state === 2" theme="warning">禁用</TTag>
25
+ <TTag v-else theme="danger">已删除</TTag>
26
+ </template>
27
+ <template #operation="{ row }">
28
+ <TDropdown trigger="click" min-column-width="120" @click="(data) => $Method.onAction(data.value, row)">
29
+ <TButton variant="text" size="small">操作</TButton>
30
+ <TDropdownMenu slot="dropdown">
31
+ <TDropdownItem value="upd">
32
+ <ILucidePencil />
33
+ 编辑
34
+ </TDropdownItem>
35
+ <TDropdownItem value="del" :divider="true">
36
+ <ILucideTrash2 style="width: 14px; height: 14px; margin-right: 6px" />
37
+ 删除
38
+ </TDropdownItem>
39
+ </TDropdownMenu>
40
+ </TDropdown>
41
+ </template>
42
+ </TTable>
43
+ </div>
44
+
45
+ <div class="main-detail">
46
+ <DetailPanel
47
+ :data="$Data.currentRow"
48
+ :fields="[
49
+ { key: 'id', label: 'ID' },
50
+ { key: 'name', label: '字典名称' },
51
+ { key: 'code', label: '字典代码' },
52
+ { key: 'value', label: '字典值' },
53
+ { key: 'pid', label: '父级ID', default: '顶级' },
54
+ { key: 'sort', label: '排序' },
55
+ { key: 'description', label: '描述' },
56
+ { key: 'state', label: '状态' }
57
+ ]"
58
+ />
59
+ </div>
42
60
  </div>
43
61
 
44
62
  <div class="main-page">
45
- <TPagination :current-page="$Data.pagerConfig.currentPage" :page-size="$Data.pagerConfig.pageSize" :total="$Data.pagerConfig.total" @current-change="$Method.onPageChange" @size-change="$Method.handleSizeChange" />
63
+ <TPagination :current-page="$Data.pagerConfig.currentPage" :page-size="$Data.pagerConfig.limit" :total="$Data.pagerConfig.total" @current-change="$Method.onPageChange" @page-size-change="$Method.handleSizeChange" />
46
64
  </div>
47
65
 
48
66
  <!-- 编辑对话框组件 -->
@@ -57,25 +75,30 @@ import ILucideRotateCw from '~icons/lucide/rotate-cw';
57
75
  import ILucidePencil from '~icons/lucide/pencil';
58
76
  import ILucideTrash2 from '~icons/lucide/trash-2';
59
77
  import EditDialog from './components/edit.vue';
78
+ import DetailPanel from '@/components/DetailPanel.vue';
60
79
  import { $Http } from '@/plugins/http';
80
+ import { withDefaultColumns, withTableProps } from '@/utils';
61
81
 
62
82
  // 响应式数据
63
83
  const $Data = $ref({
64
84
  dictList: [],
65
- columns: [
85
+ loading: false,
86
+ activeRowKeys: [],
87
+ currentRow: null,
88
+ columns: withDefaultColumns([
66
89
  { colKey: 'index', title: '序号', width: 60, align: 'center' },
67
90
  { colKey: 'name', title: '字典名称' },
68
91
  { colKey: 'code', title: '字典代码', width: 150 },
69
92
  { colKey: 'value', title: '字典值', width: 200 },
70
93
  { colKey: 'pid', title: '父级ID', width: 100 },
71
94
  { colKey: 'sort', title: '排序', width: 80 },
72
- { colKey: 'description', title: '描述', ellipsis: true },
73
- { colKey: 'state', title: '状态', width: 100 },
74
- { colKey: 'operation', title: '操作', width: 120, align: 'right' }
75
- ],
95
+ { colKey: 'description', title: '描述' },
96
+ { colKey: 'state', title: '状态', width: 100, ellipsis: false },
97
+ { colKey: 'operation', title: '操作', width: 120, align: 'right', ellipsis: false }
98
+ ]),
76
99
  pagerConfig: {
77
100
  currentPage: 1,
78
- pageSize: 30,
101
+ limit: 30,
79
102
  total: 0,
80
103
  align: 'right',
81
104
  layout: 'total, prev, pager, next, jumper'
@@ -93,19 +116,28 @@ const $Method = {
93
116
 
94
117
  // 加载字典列表
95
118
  async apiDictList() {
119
+ $Data.loading = true;
96
120
  try {
97
121
  const res = await $Http('/addon/admin/dict/list', {
98
122
  page: $Data.pagerConfig.currentPage,
99
- limit: $Data.pagerConfig.pageSize
123
+ limit: $Data.pagerConfig.limit
100
124
  });
101
125
  $Data.dictList = res.data.lists || [];
102
126
  $Data.pagerConfig.total = res.data.total || 0;
127
+
128
+ // 自动选中并高亮第一行
129
+ if ($Data.dictList.length > 0) {
130
+ $Data.currentRow = $Data.dictList[0];
131
+ $Data.activeRowKeys = [$Data.dictList[0].id];
132
+ } else {
133
+ $Data.currentRow = null;
134
+ $Data.activeRowKeys = [];
135
+ }
103
136
  } catch (error) {
104
137
  console.error('加载字典列表失败:', error);
105
- MessagePlugin.info({
106
- message: '加载数据失败',
107
- status: 'error'
108
- });
138
+ MessagePlugin.error('加载数据失败');
139
+ } finally {
140
+ $Data.loading = false;
109
141
  }
110
142
  },
111
143
 
@@ -113,20 +145,20 @@ const $Method = {
113
145
  async apiDictDel(row) {
114
146
  DialogPlugin.confirm({
115
147
  header: '确认删除',
116
- body: `确定要删除字典"${row.name}" 吗?`,
148
+ body: `确定要删除字典“${row.name} 吗?`,
117
149
  status: 'warning'
118
150
  }).then(async () => {
119
151
  try {
120
152
  const res = await $Http('/addon/admin/dict/del', { id: row.id });
121
153
  if (res.code === 0) {
122
- MessagePlugin.info({ message: '删除成功', status: 'success' });
154
+ MessagePlugin.success('删除成功');
123
155
  $Method.apiDictList();
124
156
  } else {
125
- MessagePlugin.info({ message: res.msg || '删除失败', status: 'error' });
157
+ MessagePlugin.error(res.msg || '删除失败');
126
158
  }
127
159
  } catch (error) {
128
160
  console.error('删除失败:', error);
129
- MessagePlugin.info({ message: '删除失败', status: 'error' });
161
+ MessagePlugin.error('删除失败');
130
162
  }
131
163
  });
132
164
  },
@@ -142,6 +174,28 @@ const $Method = {
142
174
  $Method.apiDictList();
143
175
  },
144
176
 
177
+ // 每页条数改变
178
+ handleSizeChange({ pageSize }) {
179
+ $Data.pagerConfig.limit = pageSize;
180
+ $Data.pagerConfig.currentPage = 1;
181
+ $Method.apiDictList();
182
+ },
183
+
184
+ // 高亮行变化(点击行选中)
185
+ onActiveChange(value, { activeRowData }) {
186
+ $Data.activeRowKeys = value;
187
+ // 更新当前高亮的行数据
188
+ if (activeRowData && activeRowData.length > 0) {
189
+ $Data.currentRow = activeRowData[0];
190
+ } else if ($Data.dictList.length > 0) {
191
+ // 如果取消高亮,默认显示第一行
192
+ $Data.currentRow = $Data.dictList[0];
193
+ $Data.activeRowKeys = [$Data.dictList[0].id];
194
+ } else {
195
+ $Data.currentRow = null;
196
+ }
197
+ },
198
+
145
199
  // 操作菜单点击
146
200
  onAction(command, rowData) {
147
201
  $Data.actionType = command;