FactoriesView.vue 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. <template>
  2. <SidebarLayout
  3. :menuItems="menuItems"
  4. :userName="authStore.user?.name"
  5. :activeKey="'factories'"
  6. @logout="handleLogout"
  7. >
  8. <div class="factories-page">
  9. <!-- 头部工具栏 -->
  10. <a-card>
  11. <a-space>
  12. <a-input-search
  13. v-model:value="searchKeyword"
  14. placeholder="搜索工厂名称"
  15. style="width: 300px"
  16. @search="loadFactories"
  17. />
  18. <a-button type="primary" @click="showCreateModal">
  19. 创建工厂
  20. </a-button>
  21. </a-space>
  22. </a-card>
  23. <!-- 工厂列表表格 -->
  24. <a-card class="mt-4">
  25. <a-table
  26. :columns="columns"
  27. :dataSource="factories"
  28. :loading="loading"
  29. :pagination="pagination"
  30. @change="handleTableChange"
  31. rowKey="id"
  32. >
  33. <!-- 状态列 -->
  34. <template #status="{ record }">
  35. <a-tag :color="record.status === 1 ? 'green' : 'red'">
  36. {{ record.status === 1 ? '启用' : '禁用' }}
  37. </a-tag>
  38. </template>
  39. <!-- 操作列 -->
  40. <template #action="{ record }">
  41. <a-space>
  42. <a-button size="small" @click="showEditModal(record)">
  43. 编辑
  44. </a-button>
  45. <a-button
  46. size="small"
  47. :type="record.status === 1 ? 'default' : 'primary'"
  48. @click="toggleFactoryStatus(record)"
  49. >
  50. {{ record.status === 1 ? '禁用' : '启用' }}
  51. </a-button>
  52. <a-popconfirm
  53. title="确定删除此工厂吗?此操作将永久删除工厂数据库文件!"
  54. ok-text="确定"
  55. cancel-text="取消"
  56. @confirm="deleteFactory(record.id)"
  57. >
  58. <a-button size="small" danger>
  59. 删除
  60. </a-button>
  61. </a-popconfirm>
  62. </a-space>
  63. </template>
  64. </a-table>
  65. </a-card>
  66. <!-- 创建/编辑工厂模态框 -->
  67. <a-modal
  68. v-model:open="modalVisible"
  69. :title="modalTitle"
  70. @ok="handleSubmit"
  71. @cancel="resetForm"
  72. :confirmLoading="submitting"
  73. >
  74. <a-form
  75. ref="formRef"
  76. :model="formState"
  77. :rules="formRules"
  78. layout="vertical"
  79. >
  80. <a-form-item label="工厂ID" name="id" v-if="!isEdit">
  81. <a-input
  82. v-model:value="formState.id"
  83. placeholder="请输入工厂ID(字母或数字)"
  84. />
  85. </a-form-item>
  86. <a-form-item label="工厂名称" name="name">
  87. <a-input
  88. v-model:value="formState.name"
  89. placeholder="请输入工厂名称"
  90. />
  91. </a-form-item>
  92. <a-form-item label="数据库路径" name="db_path" v-if="!isEdit">
  93. <a-input
  94. v-model:value="formState.db_path"
  95. placeholder="可选,默认自动生成"
  96. />
  97. </a-form-item>
  98. </a-form>
  99. </a-modal>
  100. </div>
  101. </SidebarLayout>
  102. </template>
  103. <script setup lang="ts">
  104. import { ref, reactive, onMounted } from 'vue';
  105. import { useRouter } from 'vue-router';
  106. import { message } from 'ant-design-vue';
  107. import { useAuthStore } from '@/stores/auth';
  108. import { SidebarLayout } from '@smartcut/shared-components';
  109. import { FactoryApi, createApiClient } from '@smartcut/api-client';
  110. import { STORAGE_KEYS } from '@smartcut/shared-utils';
  111. import type { Factory } from '@smartcut/types';
  112. const router = useRouter();
  113. const authStore = useAuthStore();
  114. // 菜单项
  115. const menuItems = [
  116. {
  117. key: 'dashboard',
  118. label: '主页看板',
  119. icon: 'DashboardOutlined',
  120. path: '/'
  121. },
  122. {
  123. key: 'factories',
  124. label: '工厂管理',
  125. icon: 'ShopOutlined',
  126. path: '/factories'
  127. },
  128. {
  129. key: 'users',
  130. label: '系统用户',
  131. icon: 'UserOutlined',
  132. path: '/users'
  133. }
  134. ];
  135. // API客户端
  136. const apiClient = createApiClient({
  137. baseURL: window.location.origin,
  138. tokenKey: STORAGE_KEYS.PLATFORM_TOKEN
  139. });
  140. const factoryApi = new FactoryApi(apiClient);
  141. // 工厂列表数据
  142. const factories = ref<Factory[]>([]);
  143. const loading = ref(false);
  144. const searchKeyword = ref('');
  145. const pagination = reactive({
  146. current: 1,
  147. pageSize: 10,
  148. total: 0,
  149. showSizeChanger: true,
  150. showTotal: (total: number) => `共 ${total} 条`
  151. });
  152. // 表格列定义
  153. const columns = [
  154. {
  155. title: '工厂ID',
  156. dataIndex: 'id',
  157. key: 'id',
  158. width: 120
  159. },
  160. {
  161. title: '工厂名称',
  162. dataIndex: 'name',
  163. key: 'name',
  164. width: 200
  165. },
  166. {
  167. title: '状态',
  168. dataIndex: 'status',
  169. key: 'status',
  170. width: 100,
  171. slots: { customRender: 'status' }
  172. },
  173. {
  174. title: '创建时间',
  175. dataIndex: 'created_at',
  176. key: 'created_at',
  177. width: 180
  178. },
  179. {
  180. title: '操作',
  181. key: 'action',
  182. fixed: 'right',
  183. width: 200,
  184. slots: { customRender: 'action' }
  185. }
  186. ];
  187. // 模态框
  188. const modalVisible = ref(false);
  189. const modalTitle = ref('创建工厂');
  190. const isEdit = ref(false);
  191. const submitting = ref(false);
  192. const formRef = ref();
  193. const formState = reactive({
  194. id: '',
  195. name: '',
  196. db_path: ''
  197. });
  198. const formRules = {
  199. id: [
  200. { required: true, message: '请输入工厂ID', trigger: 'blur' },
  201. { pattern: /^[a-zA-Z0-9_-]+$/, message: '工厂ID只能包含字母、数字、下划线或横线', trigger: 'blur' }
  202. ],
  203. name: [
  204. { required: true, message: '请输入工厂名称', trigger: 'blur' }
  205. ]
  206. };
  207. // 加载工厂列表
  208. async function loadFactories() {
  209. loading.value = true;
  210. try {
  211. const result = await factoryApi.listFactories();
  212. factories.value = result.list;
  213. pagination.total = result.total;
  214. } catch (error) {
  215. message.error('加载工厂列表失败');
  216. } finally {
  217. loading.value = false;
  218. }
  219. }
  220. // 表格分页/筛选变化
  221. function handleTableChange(pag: any) {
  222. pagination.current = pag.current;
  223. pagination.pageSize = pag.pageSize;
  224. loadFactories();
  225. }
  226. // 显示创建模态框
  227. function showCreateModal() {
  228. modalTitle.value = '创建工厂';
  229. isEdit.value = false;
  230. modalVisible.value = true;
  231. resetForm();
  232. }
  233. // 显示编辑模态框
  234. function showEditModal(record: Factory) {
  235. modalTitle.value = '编辑工厂';
  236. isEdit.value = true;
  237. modalVisible.value = true;
  238. // 填充表单数据
  239. formState.id = record.id;
  240. formState.name = record.name;
  241. formState.db_path = record.db_path;
  242. }
  243. // 提交表单
  244. async function handleSubmit() {
  245. try {
  246. await formRef.value.validate();
  247. submitting.value = true;
  248. if (isEdit.value) {
  249. // 编辑工厂
  250. await factoryApi.updateFactory(formState.id, {
  251. name: formState.name
  252. });
  253. message.success('工厂已更新');
  254. } else {
  255. // 创建工厂
  256. await factoryApi.createFactory({
  257. id: formState.id,
  258. name: formState.name,
  259. db_path: formState.db_path
  260. });
  261. message.success('工厂已创建');
  262. }
  263. modalVisible.value = false;
  264. loadFactories();
  265. } catch (error) {
  266. if (error.response?.data?.msg) {
  267. message.error(error.response.data.msg);
  268. } else {
  269. message.error(isEdit.value ? '更新工厂失败' : '创建工厂失败');
  270. }
  271. } finally {
  272. submitting.value = false;
  273. }
  274. }
  275. // 重置表单
  276. function resetForm() {
  277. formRef.value?.resetFields();
  278. formState.id = '';
  279. formState.name = '';
  280. formState.db_path = '';
  281. }
  282. // 切换工厂状态(启用/禁用)
  283. async function toggleFactoryStatus(record: Factory) {
  284. try {
  285. if (record.status === 1) {
  286. // 禁用工厂
  287. await factoryApi.deleteFactory(record.id, false);
  288. message.success('工厂已禁用');
  289. } else {
  290. // 启用工厂
  291. await factoryApi.enableFactory(record.id);
  292. message.success('工厂已启用');
  293. }
  294. loadFactories();
  295. } catch (error) {
  296. message.error('操作失败');
  297. }
  298. }
  299. // 删除工厂(永久删除)
  300. async function deleteFactory(id: string) {
  301. try {
  302. await factoryApi.deleteFactory(id, true);
  303. message.success('工厂已永久删除');
  304. loadFactories();
  305. } catch (error) {
  306. message.error('删除工厂失败');
  307. }
  308. }
  309. // 登出处理
  310. function handleLogout() {
  311. authStore.logout();
  312. message.success('已退出登录');
  313. router.push('/login');
  314. }
  315. // 初始化加载
  316. onMounted(() => {
  317. loadFactories();
  318. });
  319. </script>
  320. <style scoped lang="postcss">
  321. .factories-page {
  322. padding: 0;
  323. }
  324. .mt-4 {
  325. margin-top: 16px;
  326. }
  327. </style>