Merge branch 'feature/new-sys' into 'master'
Feature/new sys See merge request workbench/y-code!2
This commit is contained in:
		
						commit
						bfa3df2a05
					
				
							
								
								
									
										6
									
								
								.env.development
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.env.development
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | |||||||
|  | # .env.development | ||||||
|  | VITE_NODE_ENV = pre | ||||||
|  | 
 | ||||||
|  | VITE_OA_BASEURL = https://oa-pre.shiyue.com | ||||||
|  | 
 | ||||||
|  | VITE_YCODE_BASEURL = https://custom-chart-pre-api.shiyue.com | ||||||
| @ -1 +1,5 @@ | |||||||
|  | VITE_NODE_ENV = prod | ||||||
|  | 
 | ||||||
| VITE_OA_BASEURL = https://oa.shiyuegame.com | VITE_OA_BASEURL = https://oa.shiyuegame.com | ||||||
|  | 
 | ||||||
|  | VITE_YCODE_BASEURL = https://custom-chart-api.shiyuegame.com | ||||||
|  | |||||||
| @ -1 +1,5 @@ | |||||||
|  | VITE_NODE_ENV = pre | ||||||
|  | 
 | ||||||
| VITE_OA_BASEURL = https://oa-pre.shiyue.com | VITE_OA_BASEURL = https://oa-pre.shiyue.com | ||||||
|  | 
 | ||||||
|  | VITE_YCODE_BASEURL = https://custom-chart-pre-api.shiyue.com | ||||||
|  | |||||||
							
								
								
									
										19
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										19
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -9,12 +9,31 @@ declare module 'vue' { | |||||||
|   export interface GlobalComponents { |   export interface GlobalComponents { | ||||||
|     ABreadcrumb: typeof import('ant-design-vue/es')['Breadcrumb'] |     ABreadcrumb: typeof import('ant-design-vue/es')['Breadcrumb'] | ||||||
|     ABreadcrumbItem: typeof import('ant-design-vue/es')['BreadcrumbItem'] |     ABreadcrumbItem: typeof import('ant-design-vue/es')['BreadcrumbItem'] | ||||||
|  |     AButton: typeof import('ant-design-vue/es')['Button'] | ||||||
|  |     ACheckbox: typeof import('ant-design-vue/es')['Checkbox'] | ||||||
|  |     ACheckboxGroup: typeof import('ant-design-vue/es')['CheckboxGroup'] | ||||||
|     AConfigProvider: typeof import('ant-design-vue/es')['ConfigProvider'] |     AConfigProvider: typeof import('ant-design-vue/es')['ConfigProvider'] | ||||||
|     ADropdown: typeof import('ant-design-vue/es')['Dropdown'] |     ADropdown: typeof import('ant-design-vue/es')['Dropdown'] | ||||||
|  |     AEmpty: typeof import('ant-design-vue/es')['Empty'] | ||||||
|     AFloatButton: typeof import('ant-design-vue/es')['FloatButton'] |     AFloatButton: typeof import('ant-design-vue/es')['FloatButton'] | ||||||
|  |     AForm: typeof import('ant-design-vue/es')['Form'] | ||||||
|  |     AFormItem: typeof import('ant-design-vue/es')['FormItem'] | ||||||
|  |     AInput: typeof import('ant-design-vue/es')['Input'] | ||||||
|  |     AInputNumber: typeof import('ant-design-vue/es')['InputNumber'] | ||||||
|     AMenu: typeof import('ant-design-vue/es')['Menu'] |     AMenu: typeof import('ant-design-vue/es')['Menu'] | ||||||
|     AMenuItem: typeof import('ant-design-vue/es')['MenuItem'] |     AMenuItem: typeof import('ant-design-vue/es')['MenuItem'] | ||||||
|  |     AModal: typeof import('ant-design-vue/es')['Modal'] | ||||||
|  |     APagination: typeof import('ant-design-vue/es')['Pagination'] | ||||||
|  |     APopconfirm: typeof import('ant-design-vue/es')['Popconfirm'] | ||||||
|  |     ASelect: typeof import('ant-design-vue/es')['Select'] | ||||||
|  |     ASelectOption: typeof import('ant-design-vue/es')['SelectOption'] | ||||||
|  |     ASpace: typeof import('ant-design-vue/es')['Space'] | ||||||
|  |     ASwitch: typeof import('ant-design-vue/es')['Switch'] | ||||||
|  |     ATable: typeof import('ant-design-vue/es')['Table'] | ||||||
|     RouterLink: typeof import('vue-router')['RouterLink'] |     RouterLink: typeof import('vue-router')['RouterLink'] | ||||||
|     RouterView: typeof import('vue-router')['RouterView'] |     RouterView: typeof import('vue-router')['RouterView'] | ||||||
|  |     Table: typeof import('./src/components/common/table.vue')['default'] | ||||||
|  |     YChart: typeof import('./src/components/common/y-chart.vue')['default'] | ||||||
|  |     YTable: typeof import('./src/components/common/y-table.vue')['default'] | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -4,7 +4,7 @@ | |||||||
|     <meta charset="UTF-8"> |     <meta charset="UTF-8"> | ||||||
|     <link rel="icon" href="/favicon.ico"> |     <link rel="icon" href="/favicon.ico"> | ||||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0"> |     <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||||
|     <title>Vite App</title> |     <title>悦码后台</title> | ||||||
|   </head> |   </head> | ||||||
|   <body> |   <body> | ||||||
|     <div id="app"></div> |     <div id="app"></div> | ||||||
|  | |||||||
							
								
								
									
										3218
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										3218
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -6,14 +6,16 @@ | |||||||
|   "scripts": { |   "scripts": { | ||||||
|     "dev": "vite --mode staging", |     "dev": "vite --mode staging", | ||||||
|     "build:pre": "vite build --mode staging", |     "build:pre": "vite build --mode staging", | ||||||
|     "build": "vite build --mode production", |     "build:pro": "vite build --mode production", | ||||||
|     "type-check": "vue-tsc --build --force", |     "type-check": "vue-tsc --build --force", | ||||||
|     "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore" |     "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore" | ||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@vueuse/core": "^10.9.0", |     "@antv/g2plot": "^2.4.31", | ||||||
|  |     "@vueuse/core": "^10.11.0", | ||||||
|     "ant-design-vue": "^4.1.2", |     "ant-design-vue": "^4.1.2", | ||||||
|     "axios": "^1.6.7", |     "axios": "^1.6.7", | ||||||
|  |     "lodash": "^4.17.21", | ||||||
|     "pinia": "^2.1.7", |     "pinia": "^2.1.7", | ||||||
|     "vue": "^3.4.15", |     "vue": "^3.4.15", | ||||||
|     "vue-router": "^4.2.5" |     "vue-router": "^4.2.5" | ||||||
|  | |||||||
							
								
								
									
										28
									
								
								src/api/preview/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/api/preview/index.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | |||||||
|  | import { get, post } from "@/utils/request"; | ||||||
|  | 
 | ||||||
|  | // 预览
 | ||||||
|  | export function preview({ modularId, fieldIds, page, perPage, filter }) { | ||||||
|  |   return post({ | ||||||
|  |     url: "api/v1/preview/view", | ||||||
|  |     data: { | ||||||
|  |       modular_id: modularId, | ||||||
|  |       field_ids: fieldIds, | ||||||
|  |       page, | ||||||
|  |       per_page: perPage, | ||||||
|  |       filter, | ||||||
|  |     }, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 查看视图
 | ||||||
|  | export function searchInfo({ previewId, page, perPage, filter }) { | ||||||
|  |   return get({ | ||||||
|  |     url: `/api/v1/preview/info`, | ||||||
|  |     params: { | ||||||
|  |       preview_id: previewId, | ||||||
|  |       page, | ||||||
|  |       per_page: perPage, | ||||||
|  |       filter, | ||||||
|  |     }, | ||||||
|  |   }); | ||||||
|  | } | ||||||
							
								
								
									
										34
									
								
								src/components/common/y-chart.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/components/common/y-chart.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | |||||||
|  | <template> | ||||||
|  |   <div class="chart-show-box"> | ||||||
|  |     <div class="switch-type"> | ||||||
|  |       <a-radio-group v-model:value="chartType"> | ||||||
|  |         <a-radio-button value="line">折线图</a-radio-button> | ||||||
|  |         <a-radio-button value="bar">柱状图</a-radio-button> | ||||||
|  |       </a-radio-group> | ||||||
|  |     </div> | ||||||
|  |     <div class="chart-wrap"> | ||||||
|  |       <Column v-if="chartType === 'bar'" /> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script setup> | ||||||
|  | import { ref } from "vue"; | ||||||
|  | import Line from "@/plugins/antv-g2plot/line.vue"; | ||||||
|  | import Column from "@/plugins/antv-g2plot/column.vue"; | ||||||
|  | 
 | ||||||
|  | const props = defineProps({ | ||||||
|  |   chartCfg: { | ||||||
|  |     type: Array, | ||||||
|  |     default: () => [], | ||||||
|  |   }, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const chartType = ref("line"); | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .chart-wrap { | ||||||
|  |   padding: 20px; | ||||||
|  | } | ||||||
|  | </style> | ||||||
							
								
								
									
										139
									
								
								src/components/common/y-table.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								src/components/common/y-table.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,139 @@ | |||||||
|  | <template> | ||||||
|  |   <div class="y-table-container"> | ||||||
|  |     <div class="y-table-filter"> | ||||||
|  |       <div | ||||||
|  |         v-for="(item, index) in filterConfig" | ||||||
|  |         :key="index" | ||||||
|  |         class="filter-item" | ||||||
|  |       > | ||||||
|  |         <span>{{ item.label }}:</span> | ||||||
|  |         <a-select | ||||||
|  |           v-if="item.type === 'select'" | ||||||
|  |           class="input-item" | ||||||
|  |           :options="item.options" | ||||||
|  |           allow-clear | ||||||
|  |           v-model:value="filterData[item.name]" | ||||||
|  |           @change="toFilt" | ||||||
|  |         ></a-select> | ||||||
|  |         <a-input | ||||||
|  |           v-if="item.type === 'text'" | ||||||
|  |           class="input-item" | ||||||
|  |           placeholder="请输入" | ||||||
|  |           allow-clear | ||||||
|  |           v-model:value="filterData[item.name]" | ||||||
|  |           @change="toFilt" | ||||||
|  |         /> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |     <div class="y-table-content"> | ||||||
|  |       <a-table | ||||||
|  |         :columns="columnConfig" | ||||||
|  |         :data-source="dataList" | ||||||
|  |         :pagination="false" | ||||||
|  |         size="small" | ||||||
|  |         bordered | ||||||
|  |       ></a-table> | ||||||
|  |       <a-pagination | ||||||
|  |         v-model:current="pageState.page" | ||||||
|  |         :total="total" | ||||||
|  |         :page-size="pageState.perPage" | ||||||
|  |         :hide-on-single-page="false" | ||||||
|  |         size="small" | ||||||
|  |         class="pagination-box" | ||||||
|  |         @change="pageChange" | ||||||
|  |       /> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script setup> | ||||||
|  | import { reactive, ref, watch } from "vue"; | ||||||
|  | import { useDebounceFn } from "@vueuse/core"; | ||||||
|  | import _ from "lodash"; | ||||||
|  | 
 | ||||||
|  | const props = defineProps({ | ||||||
|  |   filterConfig: { | ||||||
|  |     type: Array, | ||||||
|  |     default: () => [], | ||||||
|  |   }, | ||||||
|  |   columnConfig: { | ||||||
|  |     type: Array, | ||||||
|  |     default: () => [], | ||||||
|  |   }, | ||||||
|  |   dataList: { | ||||||
|  |     type: Array, | ||||||
|  |     default: () => [], | ||||||
|  |   }, | ||||||
|  |   total: { | ||||||
|  |     type: Number, | ||||||
|  |     default: 0, | ||||||
|  |   }, | ||||||
|  | }); | ||||||
|  | const emit = defineEmits(["handleFilt"]); | ||||||
|  | 
 | ||||||
|  | const filterData = ref({}); | ||||||
|  | 
 | ||||||
|  | const pageState = reactive({ | ||||||
|  |   page: 1, | ||||||
|  |   perPage: 20, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | watch( | ||||||
|  |   () => props.filterConfig, | ||||||
|  |   (newVal) => { | ||||||
|  |     console.log("newVal", newVal); | ||||||
|  | 
 | ||||||
|  |     newVal.forEach((item) => { | ||||||
|  |       filterData.value[item.name] = undefined; | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | const getData = () => { | ||||||
|  |   const cloneFilter = _.cloneDeep(props.filterConfig); | ||||||
|  |   const filter = cloneFilter | ||||||
|  |     .filter((item) => { | ||||||
|  |       return filterData.value[item.name] !== undefined; | ||||||
|  |     }) | ||||||
|  |     .map((item) => { | ||||||
|  |       return { | ||||||
|  |         name: item.name, | ||||||
|  |         type: item.type, | ||||||
|  |         value: filterData.value[item.name], | ||||||
|  |       }; | ||||||
|  |     }); | ||||||
|  |   emit("toFilt", { | ||||||
|  |     filter, | ||||||
|  |     page: pageState.page, | ||||||
|  |     perPage: pageState.perPage, | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const toFilt = useDebounceFn(() => { | ||||||
|  |   getData(); | ||||||
|  | }, 500); | ||||||
|  | 
 | ||||||
|  | const pageChange = () => { | ||||||
|  |   getData(); | ||||||
|  | }; | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .y-table-filter { | ||||||
|  |   display: flex; | ||||||
|  |   flex-wrap: wrap; | ||||||
|  | } | ||||||
|  | .filter-item { | ||||||
|  |   margin-right: 10px; | ||||||
|  |   margin-bottom: 6px; | ||||||
|  | } | ||||||
|  | .input-item { | ||||||
|  |   width: 180px; | ||||||
|  | } | ||||||
|  | .y-table-content { | ||||||
|  |   margin-top: 10px; | ||||||
|  | } | ||||||
|  | .pagination-box { | ||||||
|  |   text-align: center; | ||||||
|  | } | ||||||
|  | </style> | ||||||
| @ -1,19 +1,21 @@ | |||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import { useRoute } from 'vue-router'; | import { useRoute } from "vue-router"; | ||||||
| import { HomeOutlined, FullscreenOutlined } from '@ant-design/icons-vue'; | import { HomeOutlined, FullscreenOutlined } from "@ant-design/icons-vue"; | ||||||
| import { useUserInfoStore } from '@/stores/useUserInfoStore'; | import { useUserInfoStore } from "@/stores/useUserInfoStore"; | ||||||
| import avatar from '@/assets/avatar.png'; | import avatar from "@/assets/avatar.png"; | ||||||
| import { OA_BASEURL } from '@/utils/request'; | import { YCODE_BASEURL } from "@/utils/request"; | ||||||
| import { logout } from '@/api/common'; | import { logout } from "@/api/common"; | ||||||
| 
 | 
 | ||||||
| const emits = defineEmits(['requestFullscreen']); | const emits = defineEmits(["requestFullscreen"]); | ||||||
| 
 | 
 | ||||||
| const route = useRoute(); | const route = useRoute(); | ||||||
| const userInfoStore = useUserInfoStore(); | const userInfoStore = useUserInfoStore(); | ||||||
| 
 | 
 | ||||||
| const handleLogout = () => { | const handleLogout = () => { | ||||||
|   logout().then(() => { |   logout().then(() => { | ||||||
|     window.location.href = `${OA_BASEURL}/login?redirect=${encodeURIComponent(window.location.href)}`; |     window.location.href = `${YCODE_BASEURL}/login?redirect=${encodeURIComponent( | ||||||
|  |       window.location.href | ||||||
|  |     )}`; | ||||||
|   }); |   }); | ||||||
| }; | }; | ||||||
| </script> | </script> | ||||||
| @ -31,12 +33,10 @@ const handleLogout = () => { | |||||||
|       <div class="fullscreen-icon-area" @click="emits('requestFullscreen')"> |       <div class="fullscreen-icon-area" @click="emits('requestFullscreen')"> | ||||||
|         <FullscreenOutlined class="fullscreen-icon" /> |         <FullscreenOutlined class="fullscreen-icon" /> | ||||||
|       </div> |       </div> | ||||||
|       <a-dropdown |       <a-dropdown placement="bottom"> | ||||||
|         placement="bottom" |         <div style="display: flex; align-items: center; cursor: pointer"> | ||||||
|       > |  | ||||||
|         <div style="display: flex;align-items: center;cursor: pointer;"> |  | ||||||
|           <img :src="userInfoStore.userInfo?.avatar || avatar" class="avatar" /> |           <img :src="userInfoStore.userInfo?.avatar || avatar" class="avatar" /> | ||||||
|           <div>{{ userInfoStore.userInfo?.alias || '-' }}</div> |           <div>{{ userInfoStore.userInfo?.alias || "-" }}</div> | ||||||
|         </div> |         </div> | ||||||
| 
 | 
 | ||||||
|         <template #overlay> |         <template #overlay> | ||||||
| @ -45,9 +45,7 @@ const handleLogout = () => { | |||||||
|               <span>退出登录</span> |               <span>退出登录</span> | ||||||
|             </a-menu-item> |             </a-menu-item> | ||||||
|             <a-menu-item> |             <a-menu-item> | ||||||
|               <a class="back-oa" :href="`${OA_BASEURL}/front/`"> |               <a class="back-oa" :href="`${OA_BASEURL}/front/`"> 返回OA </a> | ||||||
|                 返回OA |  | ||||||
|               </a> |  | ||||||
|             </a-menu-item> |             </a-menu-item> | ||||||
|           </a-menu> |           </a-menu> | ||||||
|         </template> |         </template> | ||||||
| @ -63,7 +61,7 @@ const handleLogout = () => { | |||||||
|   align-items: center; |   align-items: center; | ||||||
|   padding: 0 30px; |   padding: 0 30px; | ||||||
|   background-color: #fff; |   background-color: #fff; | ||||||
|   box-shadow: 0 0 12px 0 rgba(0,0,0,.08); |   box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.08); | ||||||
|   .user-area { |   .user-area { | ||||||
|     display: flex; |     display: flex; | ||||||
|     align-items: center; |     align-items: center; | ||||||
|  | |||||||
| @ -1,21 +1,25 @@ | |||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import { useUserInfoStore } from '@/stores/useUserInfoStore'; | import { useUserInfoStore } from "@/stores/useUserInfoStore"; | ||||||
| import { onMounted, ref } from 'vue'; | import { onMounted, ref } from "vue"; | ||||||
| import Header from './components/Header.vue'; | import Header from "./components/Header.vue"; | ||||||
| import Sider from './components/Sider.vue'; | import Sider from "./components/Sider.vue"; | ||||||
| import { MenuFoldOutlined, MenuUnfoldOutlined, FullscreenExitOutlined } from '@ant-design/icons-vue'; | import { | ||||||
| import { useEventListener } from '@vueuse/core'; |   MenuFoldOutlined, | ||||||
|  |   MenuUnfoldOutlined, | ||||||
|  |   FullscreenExitOutlined, | ||||||
|  | } from "@ant-design/icons-vue"; | ||||||
|  | import { useEventListener } from "@vueuse/core"; | ||||||
| 
 | 
 | ||||||
| const userInfoStore = useUserInfoStore(); | // const userInfoStore = useUserInfoStore(); | ||||||
| const isCollapsed = ref(false); | const isCollapsed = ref(false); | ||||||
| const isFullscreen = ref(false); | const isFullscreen = ref(false); | ||||||
| const container = ref<HTMLDivElement>(); | const container = ref<HTMLDivElement>(); | ||||||
| 
 | 
 | ||||||
| onMounted(() => { | onMounted(() => { | ||||||
|   userInfoStore.fetchUserInfo(); |   // userInfoStore.fetchUserInfo(); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| useEventListener(window, 'fullscreenchange', () => { | useEventListener(window, "fullscreenchange", () => { | ||||||
|   isFullscreen.value = !!document.fullscreenElement; |   isFullscreen.value = !!document.fullscreenElement; | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| @ -31,17 +35,32 @@ const handleExitFullscreen = () => { | |||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
|   <section class="root"> |   <section class="root"> | ||||||
|  |     <section | ||||||
|  |       class="left-aside" | ||||||
|  |       :class="{ 'left-aside-collapsed': isCollapsed }" | ||||||
|  |     > | ||||||
|  |       <Sider :inlineCollapsed="isCollapsed" /> | ||||||
|  |       <div class="collapsed-icon"> | ||||||
|  |         <component | ||||||
|  |           :is="isCollapsed ? MenuUnfoldOutlined : MenuFoldOutlined" | ||||||
|  |           @click="isCollapsed = !isCollapsed" | ||||||
|  |         /> | ||||||
|  |       </div> | ||||||
|  |     </section> | ||||||
|  |     <section | ||||||
|  |       class="container" | ||||||
|  |       :class="{ | ||||||
|  |         'container-fullscreen': isFullscreen, | ||||||
|  |         'container-collapsed': isCollapsed, | ||||||
|  |       }" | ||||||
|  |       ref="container" | ||||||
|  |     > | ||||||
|       <header class="header"> |       <header class="header"> | ||||||
|         <Header @requestFullscreen="handleFullscreen" /> |         <Header @requestFullscreen="handleFullscreen" /> | ||||||
|       </header> |       </header> | ||||||
|     <section class="left-aside" :class="{ 'left-aside-collapsed': isCollapsed }"> |       <div class="i-container"> | ||||||
|       <Sider :inlineCollapsed="isCollapsed" /> |  | ||||||
|       <div class="collapsed-icon"> |  | ||||||
|         <component :is="isCollapsed ? MenuUnfoldOutlined : MenuFoldOutlined" @click="isCollapsed = !isCollapsed" /> |  | ||||||
|       </div> |  | ||||||
|     </section> |  | ||||||
|     <section class="container" :class="{ 'container-fullscreen': isFullscreen, 'container-collapsed': isCollapsed }" ref="container"> |  | ||||||
|         <router-view /> |         <router-view /> | ||||||
|  |       </div> | ||||||
|       <a-float-button @click="handleExitFullscreen" v-if="isFullscreen"> |       <a-float-button @click="handleExitFullscreen" v-if="isFullscreen"> | ||||||
|         <template #icon> |         <template #icon> | ||||||
|           <FullscreenExitOutlined /> |           <FullscreenExitOutlined /> | ||||||
| @ -62,21 +81,18 @@ const handleExitFullscreen = () => { | |||||||
|   .header { |   .header { | ||||||
|     height: @header-height; |     height: @header-height; | ||||||
|     position: fixed; |     position: fixed; | ||||||
|     width: 100%; |     width: calc(100% - 220px); | ||||||
|     z-index: 1; |     z-index: 1; | ||||||
|     top: 0; |     top: 0; | ||||||
|   } |   } | ||||||
|   .left-aside { |   .left-aside { | ||||||
|     width: @aside-width; |     width: @aside-width; | ||||||
|     position: fixed; |     position: fixed; | ||||||
|     top: @header-height; |  | ||||||
|     left: 0; |     left: 0; | ||||||
|     z-index: 1; |     z-index: 1; | ||||||
|     height: calc(100% - @header-height - @header-margin); |     height: 100vh; | ||||||
|     overflow-y: hidden; |     overflow-y: hidden; | ||||||
|     margin-top: @header-margin; |     box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.08); | ||||||
|     border-radius: 8px; |  | ||||||
|     box-shadow: 0 0 12px 0 rgba(0,0,0,.08); |  | ||||||
|     display: flex; |     display: flex; | ||||||
|     flex-direction: column; |     flex-direction: column; | ||||||
|     background-color: #fff; |     background-color: #fff; | ||||||
| @ -90,7 +106,7 @@ const handleExitFullscreen = () => { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   .container { |   .container { | ||||||
|     padding-left: @aside-width + 8px; |     padding-left: @aside-width; | ||||||
|     padding-top: @header-height + @header-margin; |     padding-top: @header-height + @header-margin; | ||||||
|     height: calc(100% - @header-height - @header-margin); |     height: calc(100% - @header-height - @header-margin); | ||||||
|     position: relative; |     position: relative; | ||||||
| @ -98,6 +114,9 @@ const handleExitFullscreen = () => { | |||||||
|     transition: padding 0.3s cubic-bezier(0.2, 0, 0, 1) 0s; |     transition: padding 0.3s cubic-bezier(0.2, 0, 0, 1) 0s; | ||||||
|     margin-right: 8px; |     margin-right: 8px; | ||||||
|   } |   } | ||||||
|  |   .i-container { | ||||||
|  |     padding: 0 8px 8px 8px; | ||||||
|  |   } | ||||||
|   .container-collapsed { |   .container-collapsed { | ||||||
|     padding-left: @aside-width-collapsed + 8px; |     padding-left: @aside-width-collapsed + 8px; | ||||||
|   } |   } | ||||||
| @ -105,7 +124,10 @@ const handleExitFullscreen = () => { | |||||||
|     padding: 0; |     padding: 0; | ||||||
|     height: 100%; |     height: 100%; | ||||||
|   } |   } | ||||||
|   :deep(:where(.css-dev-only-do-not-override-1hsjdkk).ant-menu-inline-collapsed), .left-aside-collapsed { |   :deep( | ||||||
|  |       :where(.css-dev-only-do-not-override-1hsjdkk).ant-menu-inline-collapsed | ||||||
|  |     ), | ||||||
|  |   .left-aside-collapsed { | ||||||
|     width: @aside-width-collapsed; |     width: @aside-width-collapsed; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										26
									
								
								src/plugins/antv-g2plot/column.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/plugins/antv-g2plot/column.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | |||||||
|  | <template> | ||||||
|  |   <div :class="className" :style="style" ref="container"></div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script setup> | ||||||
|  | import { Column } from "@antv/g2plot"; | ||||||
|  | // hooks | ||||||
|  | import useChart from "./useChart"; | ||||||
|  | 
 | ||||||
|  | const props = defineProps({ | ||||||
|  |   className: { | ||||||
|  |     type: String, | ||||||
|  |     default: "", | ||||||
|  |   }, | ||||||
|  |   style: { | ||||||
|  |     type: Object, | ||||||
|  |     default: () => ({}), | ||||||
|  |   }, | ||||||
|  |   config: { | ||||||
|  |     type: Object, | ||||||
|  |     default: () => ({}), | ||||||
|  |   }, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const { container } = useChart(Column, props.config); | ||||||
|  | </script> | ||||||
							
								
								
									
										26
									
								
								src/plugins/antv-g2plot/line.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/plugins/antv-g2plot/line.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | |||||||
|  | <template> | ||||||
|  |   <div :class="className" :style="style" ref="container"></div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script setup> | ||||||
|  | import { Line } from "@antv/g2plot"; | ||||||
|  | // hooks | ||||||
|  | import useChart from "./useChart"; | ||||||
|  | 
 | ||||||
|  | const props = defineProps({ | ||||||
|  |   className: { | ||||||
|  |     type: String, | ||||||
|  |     default: "", | ||||||
|  |   }, | ||||||
|  |   style: { | ||||||
|  |     type: Object, | ||||||
|  |     default: () => ({}), | ||||||
|  |   }, | ||||||
|  |   config: { | ||||||
|  |     type: Object, | ||||||
|  |     default: () => ({}), | ||||||
|  |   }, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const { container } = useChart(Line, props.config); | ||||||
|  | </script> | ||||||
							
								
								
									
										26
									
								
								src/plugins/antv-g2plot/pie.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/plugins/antv-g2plot/pie.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | |||||||
|  | <template> | ||||||
|  |   <div :class="className" :style="style" ref="container"></div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script setup> | ||||||
|  | import { Pie } from "@antv/g2plot"; | ||||||
|  | // hooks | ||||||
|  | import useChart from "./useChart"; | ||||||
|  | 
 | ||||||
|  | const props = defineProps({ | ||||||
|  |   className: { | ||||||
|  |     type: String, | ||||||
|  |     default: "", | ||||||
|  |   }, | ||||||
|  |   style: { | ||||||
|  |     type: Object, | ||||||
|  |     default: () => ({}), | ||||||
|  |   }, | ||||||
|  |   config: { | ||||||
|  |     type: Object, | ||||||
|  |     default: () => ({}), | ||||||
|  |   }, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const { container } = useChart(Pie, props.config); | ||||||
|  | </script> | ||||||
							
								
								
									
										91
									
								
								src/plugins/antv-g2plot/useChart.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								src/plugins/antv-g2plot/useChart.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,91 @@ | |||||||
|  | import { onBeforeUnmount, onMounted, ref, watch } from "vue"; | ||||||
|  | import _ from "lodash"; | ||||||
|  | 
 | ||||||
|  | export default function useChart(ChartClass, config) { | ||||||
|  |   const chart = ref(null); // 表格实例
 | ||||||
|  |   const chartOptions = ref(null); // 图表配置
 | ||||||
|  |   const container = ref(null); // 渲染图表元素
 | ||||||
|  |   const { onReady, onEvent } = config; | ||||||
|  | 
 | ||||||
|  |   // 全局事件侦听器
 | ||||||
|  |   let handler; | ||||||
|  | 
 | ||||||
|  |   onMounted(() => { | ||||||
|  |     chartOptions.value = _.cloneDeep(config); | ||||||
|  | 
 | ||||||
|  |     // 实例化图表
 | ||||||
|  |     const chartInstance = new ChartClass(container.value, { ...config }); | ||||||
|  |     chartInstance.toDataURL = (type, encoderOptions) => { | ||||||
|  |       return toDataURL(type, encoderOptions); | ||||||
|  |     }; | ||||||
|  |     chartInstance.downloadImage = (name, type, encoderOptions) => { | ||||||
|  |       return downloadImage(name, type, encoderOptions); | ||||||
|  |     }; | ||||||
|  |     chartInstance.render(); // 渲染图表
 | ||||||
|  | 
 | ||||||
|  |     chart.value = chartInstance; | ||||||
|  | 
 | ||||||
|  |     // 图表渲染完成回调
 | ||||||
|  |     if (onReady) { | ||||||
|  |       onReady(chartInstance); | ||||||
|  |     } | ||||||
|  |     // 侦听全局事件
 | ||||||
|  |     handler = (event) => { | ||||||
|  |       if (onEvent) { | ||||||
|  |         onEvent(chartInstance, event); | ||||||
|  |       } | ||||||
|  |     }; | ||||||
|  |     chartInstance.on("*", handler); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   onBeforeUnmount(() => { | ||||||
|  |     chart.value.destroy(); | ||||||
|  |     chart.value.off("*", handler); | ||||||
|  |     chart.value = undefined; | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   // 配置更改时更新图表
 | ||||||
|  |   watch( | ||||||
|  |     () => config, | ||||||
|  |     (config) => { | ||||||
|  |       const newConfig = _.cloneDeep(config); | ||||||
|  |       chartOptions.value = newConfig; | ||||||
|  |       chart.value.update(newConfig); | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       deep: true, | ||||||
|  |     } | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
|  |   const toDataURL = (type = "image/png", encoderOptions) => { | ||||||
|  |     return chart.value?.chart.canvas.cfg.el.toDataURL(type, encoderOptions); | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   const downloadImage = ( | ||||||
|  |     name = "download", | ||||||
|  |     type = "image/png", | ||||||
|  |     encoderOptions | ||||||
|  |   ) => { | ||||||
|  |     let imageName = name; | ||||||
|  |     if (name.indexOf(".") === -1) { | ||||||
|  |       imageName = `${name}.${type.split("/")[1]}`; | ||||||
|  |     } | ||||||
|  |     const base64 = chart.value?.chart.canvas.cfg.el.toDataURL( | ||||||
|  |       type, | ||||||
|  |       encoderOptions | ||||||
|  |     ); | ||||||
|  |     let a = document.createElement("a"); | ||||||
|  |     a.href = base64; | ||||||
|  |     a.download = imageName; | ||||||
|  |     document.body.appendChild(a); | ||||||
|  |     a.click(); | ||||||
|  |     document.body.removeChild(a); | ||||||
|  |     a = null; | ||||||
|  |     return imageName; | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   return { | ||||||
|  |     chart, | ||||||
|  |     container, | ||||||
|  |   }; | ||||||
|  | } | ||||||
| @ -2,7 +2,7 @@ import type { NavigationGuardWithThis } from 'vue-router'; | |||||||
| 
 | 
 | ||||||
| const titleGuard: NavigationGuardWithThis<undefined> = (to, from, next) => { | const titleGuard: NavigationGuardWithThis<undefined> = (to, from, next) => { | ||||||
|   next(); |   next(); | ||||||
|   document.title = to.meta.title ? `${to.meta.title} | 机制系统` : '机制系统'; |   document.title = to.meta.title ? `${to.meta.title} | 悦码` : '悦码'; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export { titleGuard }; | export { titleGuard }; | ||||||
|  | |||||||
| @ -28,41 +28,57 @@ const routeList: RouteType[] = [ | |||||||
|         name: '-', |         name: '-', | ||||||
|         meta: {}, |         meta: {}, | ||||||
|         children: [], |         children: [], | ||||||
|         redirect: '/flow-manager/list', |         redirect: '/config-manage/project-cfg', | ||||||
|       }, |       }, | ||||||
|       { |       { | ||||||
|         path: '/flow-manager', |         path: '/config-manage', | ||||||
|         name: 'flow-manager', |         name: 'config-manage', | ||||||
|         isMenu: true, |         isMenu: true, | ||||||
|         meta: { title: '机制管理' }, |         meta: { title: '配置管理' }, | ||||||
|         icon: () => h(HomeOutlined), |         icon: () => h(HomeOutlined), | ||||||
|         children: [ |         children: [ | ||||||
|           { |           { | ||||||
|             path: 'list', |             path: 'project-cfg', | ||||||
|             name: 'list', |             name: 'project-cfg', | ||||||
|             component: () => import('@/views/flow-manager/list/index.vue'), |             component: () => import('@/views/config-manage/project-cfg/index.vue'), | ||||||
|             meta: { title: '机制列表' }, |             meta: { title: '项目配置' }, | ||||||
|             isMenu: true, |             isMenu: true, | ||||||
|             children: [], |             children: [], | ||||||
|           }, |           }, | ||||||
|           { |           { | ||||||
|             path: 'create', |             path: 'module-cfg', | ||||||
|             name: 'create', |             name: 'module-cfg', | ||||||
|             component: () => import('@/views/flow-manager/create/index.vue'), |             component: () => import('@/views/config-manage/module-cfg/index.vue'), | ||||||
|             meta: { title: '机制创建' }, |             meta: { title: '数据表配置' }, | ||||||
|             isMenu: true, |             isMenu: true, | ||||||
|             children: [], |             children: [], | ||||||
|           }, |           }, | ||||||
|         ], |         ], | ||||||
|       }, |       }, | ||||||
|       { |       { | ||||||
|         path: '/data-overview', |         path: '/view-all-manage', | ||||||
|         name: 'data-overview', |         name: 'view-all-manage', | ||||||
|         isMenu: true, |         isMenu: true, | ||||||
|         meta: { title: '数据总览' }, |         meta: { title: '视图管理' }, | ||||||
|         children: [], |  | ||||||
|         icon: () => h(BarChartOutlined), |         icon: () => h(BarChartOutlined), | ||||||
|         component: () => import('@/views/data-overview/index.vue'), |         children: [ | ||||||
|  |           { | ||||||
|  |             path: 'view-list', | ||||||
|  |             name: 'view-list', | ||||||
|  |             component: () => import('@/views/view-all-manage/view-list/index.vue'), | ||||||
|  |             meta: { title: '视图列表' }, | ||||||
|  |             isMenu: true, | ||||||
|  |             children: [], | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             path: 'create-view', | ||||||
|  |             name: 'create-view', | ||||||
|  |             component: () => import('@/views/view-all-manage/create-view/index.vue'), | ||||||
|  |             meta: { title: '创建视图' }, | ||||||
|  |             isMenu: true, | ||||||
|  |             children: [], | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|       }, |       }, | ||||||
|     ], |     ], | ||||||
|   }, |   }, | ||||||
|  | |||||||
| @ -8,10 +8,10 @@ export interface ResopnseType<T> { | |||||||
|   data: T |   data: T | ||||||
|   ts: string |   ts: string | ||||||
| } | } | ||||||
| export const OA_BASEURL: string = import.meta.env.VITE_OA_BASEURL; | export const YCODE_BASEURL: string = import.meta.env.VITE_YCODE_BASEURL; | ||||||
| 
 | 
 | ||||||
| const requestType  = { | const requestType  = { | ||||||
|   base: OA_BASEURL, |   base: YCODE_BASEURL, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const baseAxios: AxiosInstance = axios.create({ | const baseAxios: AxiosInstance = axios.create({ | ||||||
| @ -26,7 +26,7 @@ const errorHandle = (error: AxiosError) => { | |||||||
|     switch (status) { |     switch (status) { | ||||||
|       case 401: |       case 401: | ||||||
|         message.warning('请先登录'); |         message.warning('请先登录'); | ||||||
|         window.location.href = `${OA_BASEURL}/login?redirect=${encodeURIComponent(window.location.href)}`; |         window.location.href = `${YCODE_BASEURL}/login?redirect=${encodeURIComponent(window.location.href)}`; | ||||||
|         break; |         break; | ||||||
|       case 403: |       case 403: | ||||||
|         message.warning('权限不足'); |         message.warning('权限不足'); | ||||||
|  | |||||||
							
								
								
									
										132
									
								
								src/views/config-manage/module-cfg/components/create-modal.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								src/views/config-manage/module-cfg/components/create-modal.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,132 @@ | |||||||
|  | <template> | ||||||
|  |   <a-modal :open="open" @ok="handleOk"> | ||||||
|  |     <a-form | ||||||
|  |       :model="formData" | ||||||
|  |       ref="formRef" | ||||||
|  |       :rules="formRules" | ||||||
|  |       :label-col="{ span: 4 }" | ||||||
|  |       :wrapper-col="{ span: 20 }" | ||||||
|  |     > | ||||||
|  |       <a-form-item label="所属项目" name="project_id"> | ||||||
|  |         <a-select | ||||||
|  |           placeholder="请选择所属项目" | ||||||
|  |           v-model:value="formData.project_id" | ||||||
|  |           :options="projectSelect" | ||||||
|  |         /> | ||||||
|  |       </a-form-item> | ||||||
|  |       <a-form-item label="数据表名称" name="modular_name"> | ||||||
|  |         <a-input | ||||||
|  |           placeholder="请输入数据表名称" | ||||||
|  |           v-model:value="formData.modular_name" | ||||||
|  |         /> | ||||||
|  |       </a-form-item> | ||||||
|  |       <a-form-item label="展示状态" name="is_show"> | ||||||
|  |         <a-switch | ||||||
|  |           v-model:checked="formData.is_show" | ||||||
|  |           :checkedValue="1" | ||||||
|  |           :unCheckedValue="0" | ||||||
|  |         /> | ||||||
|  |       </a-form-item> | ||||||
|  |       <a-form-item label="展示类型" name="show_type_id"> | ||||||
|  |         <a-select | ||||||
|  |           placeholder="请选择展示类型" | ||||||
|  |           v-model:value="formData.show_type_id" | ||||||
|  |           :options="showTypes" | ||||||
|  |         /> | ||||||
|  |       </a-form-item> | ||||||
|  |       <a-form-item label="sql数据源" name="original_sql"> | ||||||
|  |         <a-input | ||||||
|  |           placeholder="请输入sql数据源" | ||||||
|  |           v-model:value="formData.original_sql" | ||||||
|  |         /> | ||||||
|  |       </a-form-item> | ||||||
|  |     </a-form> | ||||||
|  |   </a-modal> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script setup> | ||||||
|  | import { onMounted, ref, watch } from "vue"; | ||||||
|  | import { getShowTypeSelect } from "@/views/config-manage/module-cfg/service"; | ||||||
|  | 
 | ||||||
|  | const props = defineProps({ | ||||||
|  |   open: { | ||||||
|  |     type: Boolean, | ||||||
|  |     default: false, | ||||||
|  |   }, | ||||||
|  |   type: { | ||||||
|  |     type: String, | ||||||
|  |     default: "add", | ||||||
|  |   }, | ||||||
|  |   data: { | ||||||
|  |     type: Object, | ||||||
|  |     default: () => ({}), | ||||||
|  |   }, | ||||||
|  |   projectSelect: { | ||||||
|  |     type: Array, | ||||||
|  |     default: () => [], | ||||||
|  |   }, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const emit = defineEmits(["ok"]); | ||||||
|  | const formRules = ref({ | ||||||
|  |   modular_name: [ | ||||||
|  |     { required: true, message: "请输入数据表名称", trigger: "submit" }, | ||||||
|  |   ], | ||||||
|  |   show_type_id: [{ required: true, message: "请选择", trigger: "submit" }], | ||||||
|  |   original_sql: [{ required: true, message: "请输入", trigger: "submit" }], | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const showTypes = ref([]); | ||||||
|  | const formRef = ref(); | ||||||
|  | const formData = ref({ | ||||||
|  |   project_id: undefined, | ||||||
|  |   modular_name: undefined, | ||||||
|  |   is_show: 0, | ||||||
|  |   show_type_id: undefined, | ||||||
|  |   original_sql: undefined, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | watch( | ||||||
|  |   () => props.data, | ||||||
|  |   (newVal) => { | ||||||
|  |     if (props.type === "add") { | ||||||
|  |       resetFormData(); | ||||||
|  |     } else { | ||||||
|  |       formData.value = { | ||||||
|  |         modular_id: newVal.modular_id, | ||||||
|  |         project_id: newVal.project_id, | ||||||
|  |         modular_name: newVal.modular_name, | ||||||
|  |         is_show: newVal.is_show, | ||||||
|  |         show_type_id: newVal.show_type_id, | ||||||
|  |         original_sql: newVal.original_sql, | ||||||
|  |       }; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | onMounted(() => { | ||||||
|  |   toGetShowType(); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const toGetShowType = () => { | ||||||
|  |   getShowTypeSelect().then((res) => { | ||||||
|  |     showTypes.value = res.data; | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const resetFormData = () => { | ||||||
|  |   formData.value = { | ||||||
|  |     project_id: undefined, | ||||||
|  |     modular_name: undefined, | ||||||
|  |     is_show: 0, | ||||||
|  |     show_type_id: undefined, | ||||||
|  |     original_sql: undefined, | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const handleOk = () => { | ||||||
|  |   formRef.value.validate().then(() => { | ||||||
|  |     emit("ok", formData.value); | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  | </script> | ||||||
							
								
								
									
										234
									
								
								src/views/config-manage/module-cfg/components/field-modal.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										234
									
								
								src/views/config-manage/module-cfg/components/field-modal.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,234 @@ | |||||||
|  | <template> | ||||||
|  |   <a-modal :open="open" title="字段管理" style="top: 30px" :footer="null"> | ||||||
|  |     <div class="field-manager"> | ||||||
|  |       <div class="header-box"> | ||||||
|  |         <a-space> | ||||||
|  |           <a-input | ||||||
|  |             v-model:value="fieldName" | ||||||
|  |             placeholder="请输入字段名称" | ||||||
|  |             allow-clear | ||||||
|  |             style="width: 200px" | ||||||
|  |             @change="search" | ||||||
|  |           /> | ||||||
|  |           <a-button type="primary" @click="addField">新建</a-button> | ||||||
|  |         </a-space> | ||||||
|  |       </div> | ||||||
|  |       <a-table | ||||||
|  |         :columns="viewCfgCols" | ||||||
|  |         :data-source="dataList" | ||||||
|  |         :pagination="false" | ||||||
|  |         size="small" | ||||||
|  |         bordered | ||||||
|  |       > | ||||||
|  |         <template #bodyCell="{ column, record }"> | ||||||
|  |           <template | ||||||
|  |             v-if=" | ||||||
|  |               [ | ||||||
|  |                 'field_name', | ||||||
|  |                 'field_title', | ||||||
|  |                 'belong_to_table', | ||||||
|  |                 'original_sql', | ||||||
|  |               ].includes(column.dataIndex) | ||||||
|  |             " | ||||||
|  |           > | ||||||
|  |             <a-input | ||||||
|  |               v-if="editableData[record.field_id]" | ||||||
|  |               v-model:value="record[column.dataIndex]" | ||||||
|  |               placeholder="请输入" | ||||||
|  |             /> | ||||||
|  |             <template v-else> | ||||||
|  |               {{ record[column.dataIndex] }} | ||||||
|  |             </template> | ||||||
|  |           </template> | ||||||
|  |           <template v-if="column.dataIndex === 'field_type_name'"> | ||||||
|  |             <a-select | ||||||
|  |               v-if="editableData[record.field_id]" | ||||||
|  |               v-model:value="record.field_type_id" | ||||||
|  |               :options="fieldTypeSel" | ||||||
|  |               placeholder="请选择" | ||||||
|  |               style="width: 160px" | ||||||
|  |             > | ||||||
|  |             </a-select> | ||||||
|  |             <template v-else> | ||||||
|  |               {{ record.field_type_name }} | ||||||
|  |             </template> | ||||||
|  |           </template> | ||||||
|  |           <template v-if="column.dataIndex === 'is_search'"> | ||||||
|  |             <a-switch | ||||||
|  |               v-if="editableData[record.field_id]" | ||||||
|  |               v-model:checked="record.is_search" | ||||||
|  |               :checkedValue="1" | ||||||
|  |               :unCheckedValue="0" | ||||||
|  |             /> | ||||||
|  |             <template v-else> | ||||||
|  |               {{ record.is_search ? "是" : "否" }} | ||||||
|  |             </template> | ||||||
|  |           </template> | ||||||
|  |           <template v-if="column.dataIndex === 'action'"> | ||||||
|  |             <a-space v-if="editableData[record.field_id]"> | ||||||
|  |               <a-button type="primary" size="small" @click="handleSave(record)" | ||||||
|  |                 >保存</a-button | ||||||
|  |               > | ||||||
|  |               <a-button size="small" @click="handleCancel(record)" | ||||||
|  |                 >取消</a-button | ||||||
|  |               > | ||||||
|  |             </a-space> | ||||||
|  |             <a-button v-else type="link" @click="handleEdit(record)" | ||||||
|  |               >修改</a-button | ||||||
|  |             > | ||||||
|  |           </template> | ||||||
|  |         </template> | ||||||
|  |       </a-table> | ||||||
|  |       <a-pagination | ||||||
|  |         :current="pageState.page" | ||||||
|  |         :page-size="pageState.perPage" | ||||||
|  |         :total="pageState.total" | ||||||
|  |         class="pagination-box" | ||||||
|  |         size="small" | ||||||
|  |         @change="pageChange" | ||||||
|  |       /> | ||||||
|  |     </div> | ||||||
|  |   </a-modal> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script setup> | ||||||
|  | import { onMounted, reactive, ref, watch } from "vue"; | ||||||
|  | import { viewCfgCols } from "@/views/config-manage/module-cfg/config"; | ||||||
|  | import { | ||||||
|  |   getFieldTypeSelect, | ||||||
|  |   getFieldList, | ||||||
|  |   // deleteField, | ||||||
|  |   saveField, | ||||||
|  |   // getFieldDetail, | ||||||
|  | } from "@/views/config-manage/module-cfg/service"; | ||||||
|  | import { message } from "ant-design-vue"; | ||||||
|  | 
 | ||||||
|  | const props = defineProps({ | ||||||
|  |   open: { | ||||||
|  |     type: Boolean, | ||||||
|  |     default: false, | ||||||
|  |   }, | ||||||
|  |   modularId: { | ||||||
|  |     type: Number, | ||||||
|  |     default: 0, | ||||||
|  |   }, | ||||||
|  | }); | ||||||
|  | const listLoading = ref(false); | ||||||
|  | const fieldName = ref(""); | ||||||
|  | const dataList = ref([]); | ||||||
|  | const fieldTypeSel = ref([]); | ||||||
|  | const pageState = reactive({ | ||||||
|  |   page: 1, | ||||||
|  |   perPage: 20, | ||||||
|  |   total: 0, | ||||||
|  | }); | ||||||
|  | const editableData = reactive({}); | ||||||
|  | 
 | ||||||
|  | watch( | ||||||
|  |   () => props.open, | ||||||
|  |   (newVal) => { | ||||||
|  |     if (newVal) { | ||||||
|  |       toGetList(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | onMounted(() => { | ||||||
|  |   toGetFieldTypes(); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | // 字段类型下拉 | ||||||
|  | const toGetFieldTypes = () => { | ||||||
|  |   getFieldTypeSelect().then((res) => { | ||||||
|  |     fieldTypeSel.value = res.data; | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 字段列表 | ||||||
|  | const toGetList = () => { | ||||||
|  |   listLoading.value = true; | ||||||
|  |   getFieldList({ | ||||||
|  |     fieldName: fieldName.value, | ||||||
|  |     modularId: props.modularId, | ||||||
|  |     page: pageState.page, | ||||||
|  |     perPage: pageState.perPage, | ||||||
|  |   }).then((res) => { | ||||||
|  |     dataList.value = res.data.list; | ||||||
|  |     pageState.total = res.data.total; | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const pageChange = () => { | ||||||
|  |   toGetList(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const search = () => { | ||||||
|  |   pageState.page = 1; | ||||||
|  |   toGetList(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const addField = () => { | ||||||
|  |   const item = { | ||||||
|  |     field_id: new Date().getTime() + "", | ||||||
|  |     field_title: undefined, | ||||||
|  |     field_name: undefined, | ||||||
|  |     is_search: 0, | ||||||
|  |     field_type_id: undefined, | ||||||
|  |     belong_to_table: undefined, | ||||||
|  |     original_sql: undefined, | ||||||
|  |     sort: 0, | ||||||
|  |   }; | ||||||
|  |   dataList.value.unshift(item); | ||||||
|  |   editableData[item.field_id] = { | ||||||
|  |     ...item, | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const handleEdit = (record) => { | ||||||
|  |   editableData[record.field_id] = { | ||||||
|  |     ...record, | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const handleCancel = (record) => { | ||||||
|  |   if (typeof record.field_id === "string") { | ||||||
|  |     dataList.value.shift(); | ||||||
|  |   } else { | ||||||
|  |     delete editableData[record.field_id]; | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const handleSave = (record) => { | ||||||
|  |   const params = { | ||||||
|  |     field_id: record.field_id, | ||||||
|  |     field_title: record.field_title, | ||||||
|  |     field_name: record.field_name, | ||||||
|  |     is_search: record.is_search, | ||||||
|  |     field_type_id: record.field_type_id, | ||||||
|  |     belong_to_table: record.belong_to_table, | ||||||
|  |     original_sql: record.original_sql, | ||||||
|  |     modular_id: props.modularId, | ||||||
|  |   }; | ||||||
|  |   if (typeof params.field_id === "string") { | ||||||
|  |     // 新建 | ||||||
|  |     delete params.field_id; | ||||||
|  |   } else { | ||||||
|  |     params.field_id = record.field_id; | ||||||
|  |   } | ||||||
|  |   saveField(params).then(() => { | ||||||
|  |     delete editableData[record.field_id]; | ||||||
|  |     message.success("保存成功"); | ||||||
|  |     toGetList(); | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .header-box { | ||||||
|  |   margin-bottom: 10px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .pagination-box { | ||||||
|  |   text-align: center; | ||||||
|  | } | ||||||
|  | </style> | ||||||
							
								
								
									
										20
									
								
								src/views/config-manage/module-cfg/config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/views/config-manage/module-cfg/config.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | |||||||
|  | export const moduleCfgCols = [ | ||||||
|  |   { dataIndex: 'modular_id', title: '编号', align: 'center'}, | ||||||
|  |   { dataIndex: 'modular_name', title: '数据表名称', align: 'center'}, | ||||||
|  |   { dataIndex: 'project_name', title: '项目名称', align: 'center'}, | ||||||
|  |   { dataIndex: 'is_show', title: '展示状态', align: 'center'}, | ||||||
|  |   { dataIndex: 'show_type_handle', title: '展示类型', align: 'center'}, | ||||||
|  |   // { dataIndex: 'sort', title: '排序', align: 'center'},
 | ||||||
|  |   { dataIndex: 'action', title: '操作', align: 'center'}, | ||||||
|  | ]; | ||||||
|  | 
 | ||||||
|  | export const viewCfgCols = [ | ||||||
|  |   { dataIndex: 'field_name', title: '字段名称', align: 'center'}, | ||||||
|  |   { dataIndex: 'field_title', title: '字段标题', align: 'center'}, | ||||||
|  |   { dataIndex: 'field_type_name', title: '字段类型', align: 'center'}, | ||||||
|  |   { dataIndex: 'is_search', title: '是否可搜索', align: 'center'}, | ||||||
|  |   { dataIndex: 'sort', title: '排序', align: 'center'}, | ||||||
|  |   { dataIndex: 'belong_to_table', title: '所属表名称', align: 'center'}, | ||||||
|  |   { dataIndex: 'original_sql', title: 'sql数据源', align: 'center'}, | ||||||
|  |   { dataIndex: 'action', title: '操作', align: 'center'}, | ||||||
|  | ]; | ||||||
							
								
								
									
										233
									
								
								src/views/config-manage/module-cfg/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										233
									
								
								src/views/config-manage/module-cfg/index.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,233 @@ | |||||||
|  | <template> | ||||||
|  |   <div class="normal-container module-cfg-box"> | ||||||
|  |     <div class="header-box"> | ||||||
|  |       <a-space> | ||||||
|  |         <a-input | ||||||
|  |           v-model:value="modularName" | ||||||
|  |           placeholder="请输入数据表名称" | ||||||
|  |           allow-clear | ||||||
|  |           style="width: 200px" | ||||||
|  |           @change="search" | ||||||
|  |         /> | ||||||
|  |         <a-select | ||||||
|  |           placeholder="请选择项目" | ||||||
|  |           v-model:value="projectId" | ||||||
|  |           :options="projectSel" | ||||||
|  |           allow-clear | ||||||
|  |           style="width: 200px" | ||||||
|  |           @change="search" | ||||||
|  |         ></a-select> | ||||||
|  |         <a-button type="primary" @click="openCreateModal">新建</a-button> | ||||||
|  |       </a-space> | ||||||
|  |     </div> | ||||||
|  |     <div class="content-box"> | ||||||
|  |       <a-table | ||||||
|  |         :columns="moduleCfgCols" | ||||||
|  |         :data-source="dataList" | ||||||
|  |         :pagination="false" | ||||||
|  |         size="small" | ||||||
|  |         bordered | ||||||
|  |       > | ||||||
|  |         <template #bodyCell="{ column, record }"> | ||||||
|  |           <template v-if="column.dataIndex === 'is_show'"> | ||||||
|  |             <a-switch | ||||||
|  |               :checked="record.is_show" | ||||||
|  |               :checkedValue="1" | ||||||
|  |               :unCheckedValue="0" | ||||||
|  |               @change="toChangeStatus(record.modular_id, $event)" | ||||||
|  |             /> | ||||||
|  |           </template> | ||||||
|  |           <template v-if="column.dataIndex === 'action'"> | ||||||
|  |             <a-space> | ||||||
|  |               <a-button type="link" size="small" @click="openFieldModal(record)" | ||||||
|  |                 >字段管理</a-button | ||||||
|  |               > | ||||||
|  |               <a-button | ||||||
|  |                 type="link" | ||||||
|  |                 size="small" | ||||||
|  |                 @click="toGetModularDetail(record.modular_id)" | ||||||
|  |                 >编辑</a-button | ||||||
|  |               > | ||||||
|  |               <a-popconfirm | ||||||
|  |                 title="确定删除吗?" | ||||||
|  |                 @confirm="toDelete(record.modular_id)" | ||||||
|  |               > | ||||||
|  |                 <a-button type="link" size="small">删除</a-button> | ||||||
|  |               </a-popconfirm> | ||||||
|  |             </a-space> | ||||||
|  |           </template> | ||||||
|  |         </template> | ||||||
|  |       </a-table> | ||||||
|  |       <a-pagination | ||||||
|  |         v-model:current="pageState.page" | ||||||
|  |         :total="pageState.total" | ||||||
|  |         :page-size="pageState.perPage" | ||||||
|  |         :hide-on-single-page="false" | ||||||
|  |         size="small" | ||||||
|  |         class="pagination-box" | ||||||
|  |         @change="toGetModularList" | ||||||
|  |       /> | ||||||
|  |     </div> | ||||||
|  |     <CreateModal | ||||||
|  |       :width="700" | ||||||
|  |       :open="modalState.visible" | ||||||
|  |       :title="modalState.title" | ||||||
|  |       :type="modalState.type" | ||||||
|  |       :data="modalState.data" | ||||||
|  |       :projectSelect="projectSel" | ||||||
|  |       @cancel="modalState.visible = false" | ||||||
|  |       @ok="toSave" | ||||||
|  |     /> | ||||||
|  |     <FieldModal | ||||||
|  |       title="字段管理" | ||||||
|  |       width="80%" | ||||||
|  |       :open="fieldModalState.visible" | ||||||
|  |       :modularId="fieldModalState.modularId" | ||||||
|  |       @cancel="fieldModalState.visible = false" | ||||||
|  |     /> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | <script setup> | ||||||
|  | import { ref, reactive, onMounted } from "vue"; | ||||||
|  | import { message } from "ant-design-vue"; | ||||||
|  | import { moduleCfgCols } from "./config"; | ||||||
|  | import { | ||||||
|  |   getModularList, | ||||||
|  |   deleteModular, | ||||||
|  |   getModularDetail, | ||||||
|  |   getProjectSelect, | ||||||
|  |   saveModular, | ||||||
|  |   updateStatus, | ||||||
|  | } from "./service"; | ||||||
|  | import CreateModal from "./components/create-modal.vue"; | ||||||
|  | import FieldModal from "./components/field-modal.vue"; | ||||||
|  | 
 | ||||||
|  | const dataList = ref([]); | ||||||
|  | const listLoading = ref(false); | ||||||
|  | const saveLoading = ref(false); | ||||||
|  | const detailLoading = ref(false); | ||||||
|  | const modularName = ref(""); | ||||||
|  | const projectId = ref(); | ||||||
|  | const projectSel = ref([]); | ||||||
|  | const modalState = reactive({ | ||||||
|  |   title: "", | ||||||
|  |   visible: false, | ||||||
|  |   type: "", // add - 新建,edit - 编辑 | ||||||
|  |   data: {}, | ||||||
|  | }); | ||||||
|  | const fieldModalState = reactive({ | ||||||
|  |   visible: false, | ||||||
|  |   title: "", | ||||||
|  |   modularId: undefined, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const pageState = reactive({ | ||||||
|  |   page: 1, | ||||||
|  |   pageSize: 20, | ||||||
|  |   total: 0, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | onMounted(() => { | ||||||
|  |   toGetModularList(); | ||||||
|  |   toGetProjectSel(); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const toGetProjectSel = () => { | ||||||
|  |   getProjectSelect().then((res) => { | ||||||
|  |     projectSel.value = res.data; | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const toGetModularList = () => { | ||||||
|  |   listLoading.value = true; | ||||||
|  |   getModularList({ | ||||||
|  |     page: pageState.page, | ||||||
|  |     perPage: pageState.pageSize, | ||||||
|  |     modularName: modularName.value, | ||||||
|  |     projectId: projectId.value, | ||||||
|  |   }).then((res) => { | ||||||
|  |     dataList.value = res.data.list; | ||||||
|  |     pageState.total = res.data.total; | ||||||
|  |     listLoading.value = false; | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 详情 | ||||||
|  | const toGetModularDetail = (modularId) => { | ||||||
|  |   detailLoading.value = true; | ||||||
|  |   modalState.visible = true; | ||||||
|  |   modalState.title = "编辑"; | ||||||
|  |   modalState.type = "edit"; | ||||||
|  |   getModularDetail({ modularId }) | ||||||
|  |     .then((res) => { | ||||||
|  |       modalState.data = res.data; | ||||||
|  |     }) | ||||||
|  |     .finally(() => { | ||||||
|  |       detailLoading.value = false; | ||||||
|  |     }); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 保存 | ||||||
|  | const toSave = (data) => { | ||||||
|  |   saveLoading.value = true; | ||||||
|  |   saveModular(data) | ||||||
|  |     .then(() => { | ||||||
|  |       message.success("保存成功"); | ||||||
|  |       modalState.visible = false; | ||||||
|  |       toGetModularList(); | ||||||
|  |     }) | ||||||
|  |     .finally(() => { | ||||||
|  |       saveLoading.value = false; | ||||||
|  |     }); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 删除 | ||||||
|  | const toDelete = (id) => { | ||||||
|  |   deleteModular({ modular_id: id }).then(() => { | ||||||
|  |     message.success("删除成功"); | ||||||
|  |     search(); | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const toChangeStatus = (id, status) => { | ||||||
|  |   updateStatus({ | ||||||
|  |     modular_id: id, | ||||||
|  |     status, | ||||||
|  |   }).then(() => { | ||||||
|  |     message.success("修改成功"); | ||||||
|  |     search(); | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 点击新建 | ||||||
|  | const openCreateModal = () => { | ||||||
|  |   modalState.visible = true; | ||||||
|  |   modalState.title = "新建"; | ||||||
|  |   modalState.type = "add"; | ||||||
|  |   modalState.data = {}; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const search = () => { | ||||||
|  |   pageState.page = 1; | ||||||
|  |   toGetModularList(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const openFieldModal = (record) => { | ||||||
|  |   fieldModalState.visible = true; | ||||||
|  |   fieldModalState.modularId = record.modular_id; | ||||||
|  | }; | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style lang="less"> | ||||||
|  | .header-box { | ||||||
|  |   margin-bottom: 10px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ant-table-wrapper { | ||||||
|  |   margin-bottom: 10px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .pagination-box { | ||||||
|  |   text-align: center; | ||||||
|  | } | ||||||
|  | </style> | ||||||
							
								
								
									
										108
									
								
								src/views/config-manage/module-cfg/service.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								src/views/config-manage/module-cfg/service.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,108 @@ | |||||||
|  | import { get, post } from "@/utils/request"; | ||||||
|  | 
 | ||||||
|  | // 获取数据表配置列表
 | ||||||
|  | export function getModularList({ page, perPage, modularName, projectId }) { | ||||||
|  |   return get({ | ||||||
|  |     url: "/api/v1/modular/list", | ||||||
|  |     params: { | ||||||
|  |       page, | ||||||
|  |       per_page: perPage, | ||||||
|  |       modular_name: modularName, | ||||||
|  |       project_id: projectId, | ||||||
|  |     }, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 获取数据表配置详情
 | ||||||
|  | export function getModularDetail({ modularId }) { | ||||||
|  |   return get({ | ||||||
|  |     url: "/api/v1/modular/info", | ||||||
|  |     params: { | ||||||
|  |       modular_id: modularId, | ||||||
|  |     }, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 保存数据库配置
 | ||||||
|  | export function saveModular(data) { | ||||||
|  |   return post({ | ||||||
|  |     url: "/api/v1/modular/save", | ||||||
|  |     data, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 删除数据库配置
 | ||||||
|  | export function deleteModular(data) { | ||||||
|  |   return post({ | ||||||
|  |     url: "/api/v1/modular/del", | ||||||
|  |     data, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 修改数据表状态
 | ||||||
|  | export function updateStatus(data) { | ||||||
|  |   return post({ | ||||||
|  |     url: "/api/v1/modular/change-status", | ||||||
|  |     data, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 项目下拉
 | ||||||
|  | export function getProjectSelect() { | ||||||
|  |   return get({ | ||||||
|  |     url: `/api/v1/project/get-project-drop`, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 展示类型下拉
 | ||||||
|  | export function getShowTypeSelect() { | ||||||
|  |   return get({ | ||||||
|  |     url: `/api/v1/modular/get-show-type-drop`, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 字段类型下拉
 | ||||||
|  | export function getFieldTypeSelect() { | ||||||
|  |   return get({ | ||||||
|  |     url: `/api/v1/field/get-field-type-drop`, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 获取字段列表
 | ||||||
|  | export function getFieldList({ modularId, fieldName, page, perPage }) { | ||||||
|  |   return get({ | ||||||
|  |     url: "/api/v1/field/list", | ||||||
|  |     params: { | ||||||
|  |       modular_id: modularId, | ||||||
|  |       field_name: fieldName, | ||||||
|  |       page, | ||||||
|  |       per_page: perPage, | ||||||
|  |     }, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 获取字段详情
 | ||||||
|  | export function getFieldDetail({ fieldId }) { | ||||||
|  |   return get({ | ||||||
|  |     url: "/api/v1/field/info", | ||||||
|  |     params: { | ||||||
|  |       field_id: fieldId, | ||||||
|  |     }, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 保存字段
 | ||||||
|  | export function saveField(data) { | ||||||
|  |   return post({ | ||||||
|  |     url: "/api/v1/field/save", | ||||||
|  |     data, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 删除字段
 | ||||||
|  | export function deleteField(data) { | ||||||
|  |   return post({ | ||||||
|  |     url: "/api/v1/field/del", | ||||||
|  |     data, | ||||||
|  |   }); | ||||||
|  | } | ||||||
							
								
								
									
										182
									
								
								src/views/config-manage/project-cfg/components/create-modal.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										182
									
								
								src/views/config-manage/project-cfg/components/create-modal.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,182 @@ | |||||||
|  | <template> | ||||||
|  |   <a-modal :open="open" @ok="handleOk"> | ||||||
|  |     <a-form | ||||||
|  |       :model="formData" | ||||||
|  |       ref="formRef" | ||||||
|  |       :rules="formRules" | ||||||
|  |       :label-col="{ span: 4 }" | ||||||
|  |       :wrapper-col="{ span: 20 }" | ||||||
|  |     > | ||||||
|  |       <a-form-item label="项目名称" name="project_name"> | ||||||
|  |         <a-input | ||||||
|  |           placeholder="请输入项目名称,例如:OA" | ||||||
|  |           v-model:value="formData.project_name" | ||||||
|  |         /> | ||||||
|  |       </a-form-item> | ||||||
|  |       <a-form-item label="项目标识" name="mark"> | ||||||
|  |         <a-input placeholder="请输入项目标识,例如:oa" /> | ||||||
|  |       </a-form-item> | ||||||
|  |       <a-form-item label="展示状态" name="is_show"> | ||||||
|  |         <a-switch | ||||||
|  |           v-model:checked="formData.is_show" | ||||||
|  |           :checkedValue="1" | ||||||
|  |           :unCheckedValue="0" | ||||||
|  |         /> | ||||||
|  |       </a-form-item> | ||||||
|  |       <a-form-item label="数据库地址" name="database_address"> | ||||||
|  |         <a-input | ||||||
|  |           placeholder="请输入数据库地址" | ||||||
|  |           v-model:value="formData.database_address" | ||||||
|  |         /> | ||||||
|  |       </a-form-item> | ||||||
|  |       <a-form-item label="数据库端口" name="database_port"> | ||||||
|  |         <a-input-number | ||||||
|  |           placeholder="请输入数据库端口" | ||||||
|  |           v-model:value="formData.database_port" | ||||||
|  |           style="width: 200px" | ||||||
|  |         /> | ||||||
|  |       </a-form-item> | ||||||
|  |       <a-form-item label="数据库名称" name="database_name"> | ||||||
|  |         <a-input | ||||||
|  |           placeholder="请输入数据库名称" | ||||||
|  |           v-model:value="formData.database_name" | ||||||
|  |         /> | ||||||
|  |       </a-form-item> | ||||||
|  |       <a-form-item label="数据库用户" name="database_username"> | ||||||
|  |         <a-input | ||||||
|  |           placeholder="请输入数据库用户" | ||||||
|  |           v-model:value="formData.database_username" | ||||||
|  |         /> | ||||||
|  |       </a-form-item> | ||||||
|  |       <a-form-item label="数据库密码" name="database_password"> | ||||||
|  |         <a-space> | ||||||
|  |           <a-input | ||||||
|  |             placeholder="请输入数据库密码" | ||||||
|  |             v-model:value="formData.database_password" | ||||||
|  |           /> | ||||||
|  |           <a-button type="primary" @click="toCheckDbConnect" | ||||||
|  |             >检测数据库连接</a-button | ||||||
|  |           > | ||||||
|  |         </a-space> | ||||||
|  |       </a-form-item> | ||||||
|  |     </a-form> | ||||||
|  |   </a-modal> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script setup> | ||||||
|  | import { ref, watch } from "vue"; | ||||||
|  | import { checkDbConnect } from "@/views/config-manage/project-cfg/service"; | ||||||
|  | import { message } from "ant-design-vue"; | ||||||
|  | 
 | ||||||
|  | const props = defineProps({ | ||||||
|  |   open: { | ||||||
|  |     type: Boolean, | ||||||
|  |     default: false, | ||||||
|  |   }, | ||||||
|  |   type: { | ||||||
|  |     type: String, | ||||||
|  |     default: "add", | ||||||
|  |   }, | ||||||
|  |   data: { | ||||||
|  |     type: Object, | ||||||
|  |     default: () => ({}), | ||||||
|  |   }, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const emit = defineEmits(["ok"]); | ||||||
|  | const formRules = { | ||||||
|  |   project_name: [ | ||||||
|  |     { required: true, message: "请输入项目名称", trigger: "submit" }, | ||||||
|  |   ], | ||||||
|  |   mark: [{ required: true, message: "请输入项目标识", trigger: "submit" }], | ||||||
|  |   database_address: [ | ||||||
|  |     { required: true, message: "请输入数据库地址", trigger: "submit" }, | ||||||
|  |   ], | ||||||
|  |   database_port: [ | ||||||
|  |     { required: true, message: "请输入数据库端口", trigger: "submit" }, | ||||||
|  |   ], | ||||||
|  |   database_name: [ | ||||||
|  |     { required: true, message: "请输入数据库名称", trigger: "submit" }, | ||||||
|  |   ], | ||||||
|  |   database_username: [ | ||||||
|  |     { required: true, message: "请输入数据库用户", trigger: "submit" }, | ||||||
|  |   ], | ||||||
|  |   database_password: [ | ||||||
|  |     { required: true, message: "请输入数据库密码", trigger: "submit" }, | ||||||
|  |   ], | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const formRef = ref(); | ||||||
|  | const formData = ref({ | ||||||
|  |   project_name: undefined, | ||||||
|  |   mark: undefined, | ||||||
|  |   is_show: 0, | ||||||
|  |   database_address: undefined, | ||||||
|  |   database_port: undefined, | ||||||
|  |   database_name: undefined, | ||||||
|  |   database_username: undefined, | ||||||
|  |   database_password: undefined, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | watch( | ||||||
|  |   () => props.data, | ||||||
|  |   (newVal) => { | ||||||
|  |     if (props.type === "add") { | ||||||
|  |       resetFormData(); | ||||||
|  |     } else { | ||||||
|  |       formData.value = newVal; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | // 检查数据库连接 | ||||||
|  | const toCheckDbConnect = () => { | ||||||
|  |   if (validateConnect()) { | ||||||
|  |     checkDbConnect({ | ||||||
|  |       database_name: formData.value.database_name, | ||||||
|  |       database_port: formData.value.database_port, | ||||||
|  |       database_address: formData.value.database_address, | ||||||
|  |       database_username: formData.value.database_username, | ||||||
|  |       database_password: formData.value.database_password, | ||||||
|  |     }).then((res) => { | ||||||
|  |       message.success(res.message); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const validateConnect = () => { | ||||||
|  |   const fields = { | ||||||
|  |     database_name: "请输入数据库名称", | ||||||
|  |     database_port: "请输入数据库端口", | ||||||
|  |     database_address: "请输入数据库地址", | ||||||
|  |     database_username: "请输入数据库用户", | ||||||
|  |     database_password: "请输入数据库密码", | ||||||
|  |   }; | ||||||
|  |   for (const key in fields) { | ||||||
|  |     if (!formData.value[key]) { | ||||||
|  |       message.error(fields[key]); | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return true; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const resetFormData = () => { | ||||||
|  |   formData.value = { | ||||||
|  |     project_name: undefined, | ||||||
|  |     mark: undefined, | ||||||
|  |     is_show: 0, | ||||||
|  |     database_address: undefined, | ||||||
|  |     database_port: undefined, | ||||||
|  |     database_name: undefined, | ||||||
|  |     database_username: undefined, | ||||||
|  |     database_password: undefined, | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const handleOk = () => { | ||||||
|  |   formRef.value.validate().then(() => { | ||||||
|  |     emit("ok", formData.value); | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  | </script> | ||||||
							
								
								
									
										9
									
								
								src/views/config-manage/project-cfg/config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/views/config-manage/project-cfg/config.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | |||||||
|  | export const projectCfgCols = [ | ||||||
|  |   { dataIndex: 'project_id', title: '编号', align: 'center'}, | ||||||
|  |   { dataIndex: 'project_name', title: '项目名称', align: 'center'}, | ||||||
|  |   { dataIndex: 'mark', title: '项目标识', align: 'center'}, | ||||||
|  |   { dataIndex: 'database_name', title: '数据库名', align: 'center'}, | ||||||
|  |   { dataIndex: 'is_show', title: '展示状态', align: 'center'}, | ||||||
|  |   // { dataIndex: 'sort', title: '排序', align: 'center'},
 | ||||||
|  |   { dataIndex: 'action', title: '操作', align: 'center'}, | ||||||
|  | ]; | ||||||
							
								
								
									
										200
									
								
								src/views/config-manage/project-cfg/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										200
									
								
								src/views/config-manage/project-cfg/index.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,200 @@ | |||||||
|  | <template> | ||||||
|  |   <div class="normal-container project-cfg-box"> | ||||||
|  |     <div class="header-box"> | ||||||
|  |       <a-space> | ||||||
|  |         <a-input | ||||||
|  |           v-model:value="projectName" | ||||||
|  |           placeholder="请输入项目名称" | ||||||
|  |           allow-clear | ||||||
|  |           style="width: 200px" | ||||||
|  |           @change="search" | ||||||
|  |         /> | ||||||
|  |         <a-button type="primary" @click="openCreateModal">新建</a-button> | ||||||
|  |       </a-space> | ||||||
|  |     </div> | ||||||
|  |     <div class="content-box"> | ||||||
|  |       <a-table | ||||||
|  |         :columns="projectCfgCols" | ||||||
|  |         :data-source="dataList" | ||||||
|  |         :pagination="false" | ||||||
|  |         size="small" | ||||||
|  |         bordered | ||||||
|  |       > | ||||||
|  |         <template #bodyCell="{ column, record }"> | ||||||
|  |           <template v-if="column.dataIndex === 'is_show'"> | ||||||
|  |             <a-switch | ||||||
|  |               v-model:checked="record.is_show" | ||||||
|  |               :checkedValue="1" | ||||||
|  |               :unCheckedValue="0" | ||||||
|  |               @change="toChangeStatus(record.project_id, $event)" | ||||||
|  |             /> | ||||||
|  |           </template> | ||||||
|  |           <template v-if="column.dataIndex === 'action'"> | ||||||
|  |             <a-space> | ||||||
|  |               <a-button | ||||||
|  |                 type="link" | ||||||
|  |                 size="small" | ||||||
|  |                 @click="toGetDetail(record.project_id)" | ||||||
|  |                 >编辑</a-button | ||||||
|  |               > | ||||||
|  |               <a-popconfirm | ||||||
|  |                 title="确定删除?" | ||||||
|  |                 @confirm="toDelete(record.project_id)" | ||||||
|  |               > | ||||||
|  |                 <a-button type="link" size="small">删除</a-button> | ||||||
|  |               </a-popconfirm> | ||||||
|  |             </a-space> | ||||||
|  |           </template> | ||||||
|  |         </template> | ||||||
|  |       </a-table> | ||||||
|  |       <a-pagination | ||||||
|  |         v-model:current="pageState.page" | ||||||
|  |         :total="pageState.total" | ||||||
|  |         :page-size="pageState.perPage" | ||||||
|  |         :hide-on-single-page="false" | ||||||
|  |         size="small" | ||||||
|  |         class="pagination-box" | ||||||
|  |         @change="toGetProjectList" | ||||||
|  |       /> | ||||||
|  |     </div> | ||||||
|  |     <CreateModal | ||||||
|  |       :width="700" | ||||||
|  |       :open="modalState.visible" | ||||||
|  |       :title="modalState.title" | ||||||
|  |       :type="modalState.type" | ||||||
|  |       :data="modalState.data" | ||||||
|  |       :confirmLoading="saveLoading" | ||||||
|  |       @cancel="modalState.visible = false" | ||||||
|  |       @ok="toSave" | ||||||
|  |     /> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script setup> | ||||||
|  | import { onMounted, ref, reactive } from "vue"; | ||||||
|  | import { message } from "ant-design-vue"; | ||||||
|  | import { projectCfgCols } from "./config"; | ||||||
|  | import { | ||||||
|  |   getProjectList, | ||||||
|  |   saveProject, | ||||||
|  |   deleteProject, | ||||||
|  |   getProjectDetail, | ||||||
|  |   updateStatus, | ||||||
|  | } from "./service"; | ||||||
|  | import CreateModal from "./components/create-modal.vue"; | ||||||
|  | 
 | ||||||
|  | const dataList = ref([]); | ||||||
|  | const listLoading = ref(false); | ||||||
|  | const saveLoading = ref(false); | ||||||
|  | const detailLoading = ref(false); | ||||||
|  | const projectName = ref(""); | ||||||
|  | 
 | ||||||
|  | const modalState = reactive({ | ||||||
|  |   title: undefined, | ||||||
|  |   visible: false, | ||||||
|  |   type: "", // add - 新建,edit - 编辑 | ||||||
|  |   data: { | ||||||
|  |     project_name: undefined, | ||||||
|  |     is_show: 0, | ||||||
|  |     database_address: undefined, | ||||||
|  |     database_port: undefined, | ||||||
|  |     database_name: undefined, | ||||||
|  |     database_username: undefined, | ||||||
|  |     database_password: undefined, | ||||||
|  |   }, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const pageState = reactive({ | ||||||
|  |   page: 1, | ||||||
|  |   perPage: 20, | ||||||
|  |   total: 0, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | onMounted(() => { | ||||||
|  |   toGetProjectList(); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | // 获取列表 | ||||||
|  | const toGetProjectList = () => { | ||||||
|  |   listLoading.value = true; | ||||||
|  |   getProjectList({ | ||||||
|  |     page: pageState.page, | ||||||
|  |     perPage: pageState.perPage, | ||||||
|  |     projectName: projectName.value, | ||||||
|  |   }) | ||||||
|  |     .then((res) => { | ||||||
|  |       dataList.value = res.data.list; | ||||||
|  |       pageState.total = res.data.total; | ||||||
|  |     }) | ||||||
|  |     .finally(() => { | ||||||
|  |       listLoading.value = false; | ||||||
|  |     }); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 保存 | ||||||
|  | const toSave = (data) => { | ||||||
|  |   saveLoading.value = true; | ||||||
|  |   saveProject(data) | ||||||
|  |     .then(() => { | ||||||
|  |       message.success("保存成功"); | ||||||
|  |       modalState.visible = false; | ||||||
|  |       toGetProjectList(); | ||||||
|  |     }) | ||||||
|  |     .finally(() => { | ||||||
|  |       saveLoading.value = false; | ||||||
|  |     }); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 获取详情 | ||||||
|  | const toGetDetail = (projectId) => { | ||||||
|  |   detailLoading.value = true; | ||||||
|  |   modalState.visible = true; | ||||||
|  |   modalState.title = "编辑"; | ||||||
|  |   modalState.type = "edit"; | ||||||
|  |   getProjectDetail({ projectId }).then((res) => { | ||||||
|  |     modalState.data = res.data; | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 删除 | ||||||
|  | const toDelete = (id) => { | ||||||
|  |   deleteProject({ projectId: id }).then(() => { | ||||||
|  |     message.success("删除成功"); | ||||||
|  |     search(); | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const toChangeStatus = (id, status) => { | ||||||
|  |   updateStatus({ project_id: id, status }).then(() => { | ||||||
|  |     message.success("修改成功"); | ||||||
|  |     search(); | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const search = () => { | ||||||
|  |   pageState.page = 1; | ||||||
|  |   toGetProjectList(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 点击新建 | ||||||
|  | const openCreateModal = () => { | ||||||
|  |   modalState.visible = true; | ||||||
|  |   modalState.title = "新建"; | ||||||
|  |   modalState.type = "add"; | ||||||
|  |   modalState.data = {}; | ||||||
|  | }; | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style lang="less" scope> | ||||||
|  | .header-box { | ||||||
|  |   margin-bottom: 10px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ant-table-wrapper { | ||||||
|  |   margin-bottom: 10px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .pagination-box { | ||||||
|  |   text-align: center; | ||||||
|  | } | ||||||
|  | </style> | ||||||
							
								
								
									
										57
									
								
								src/views/config-manage/project-cfg/service.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/views/config-manage/project-cfg/service.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,57 @@ | |||||||
|  | import { get, post } from "@/utils/request"; | ||||||
|  | 
 | ||||||
|  | // 获取项目列表
 | ||||||
|  | export function getProjectList({ page, perPage, projectName }) { | ||||||
|  |   return get({ | ||||||
|  |     url: `/api/v1/project/list`, | ||||||
|  |     params: { | ||||||
|  |       page, | ||||||
|  |       per_page: perPage, | ||||||
|  |       project_name: projectName, | ||||||
|  |     }, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 获取项目详情
 | ||||||
|  | export function getProjectDetail({ projectId }) { | ||||||
|  |   return get({ | ||||||
|  |     url: `api/v1/project/info`, | ||||||
|  |     params: { | ||||||
|  |       project_id: projectId, | ||||||
|  |     }, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 保存
 | ||||||
|  | export function saveProject(data) { | ||||||
|  |   return post({ | ||||||
|  |     url: `/api/v1/project/save`, | ||||||
|  |     data, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 删除
 | ||||||
|  | export function deleteProject({ projectId }) { | ||||||
|  |   return post({ | ||||||
|  |     url: `/api/v1/project/del`, | ||||||
|  |     data: { | ||||||
|  |       project_id: projectId, | ||||||
|  |     }, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 检测数据库链接
 | ||||||
|  | export function checkDbConnect(data) { | ||||||
|  |   return post({ | ||||||
|  |     url: `/api/v1/project/check-database-connect`, | ||||||
|  |     data, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 修改展示状态
 | ||||||
|  | export function updateStatus(data) { | ||||||
|  |   return post({ | ||||||
|  |     url: `/api/v1/project/change-status`, | ||||||
|  |     data, | ||||||
|  |   }); | ||||||
|  | } | ||||||
							
								
								
									
										110
									
								
								src/views/config-manage/view-cfg/components/create-modal.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								src/views/config-manage/view-cfg/components/create-modal.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,110 @@ | |||||||
|  | <template> | ||||||
|  |   <a-modal @ok="handleOk"> | ||||||
|  |     <a-form | ||||||
|  |       :model="formData" | ||||||
|  |       ref="formRef" | ||||||
|  |       :rules="formRules" | ||||||
|  |       :label-col="{ span: 4 }" | ||||||
|  |       :wrapper-col="{ span: 20 }" | ||||||
|  |     > | ||||||
|  |       <a-form-item label="字段标题" name="field_title"> | ||||||
|  |         <a-input | ||||||
|  |           placeholder="请输入字段标题" | ||||||
|  |           v-model:value="formData.field_title" | ||||||
|  |         /> | ||||||
|  |       </a-form-item> | ||||||
|  |       <a-form-item label="字段名称" name="field_name"> | ||||||
|  |         <a-input placeholder="请输入字段名称" v-model="formData.field_name" /> | ||||||
|  |       </a-form-item> | ||||||
|  |       <a-form-item label="搜索状态" name="is_search"> | ||||||
|  |         <a-switch | ||||||
|  |           v-model:checked="formData.is_search" | ||||||
|  |           :checkedValue="1" | ||||||
|  |           :unCheckedValue="0" | ||||||
|  |         /> | ||||||
|  |       </a-form-item> | ||||||
|  |       <a-form-item label="字段类型" name="field_type_id"> | ||||||
|  |         <a-select | ||||||
|  |           placeholder="请选择字段类型" | ||||||
|  |           v-model:value="formData.field_type_id" | ||||||
|  |         /> | ||||||
|  |       </a-form-item> | ||||||
|  |       <a-form-item label="所属表名称" name="belong_to_table"> | ||||||
|  |         <a-input | ||||||
|  |           placeholder="请输入所属表名称" | ||||||
|  |           v-model="formData.belong_to_table" | ||||||
|  |         /> | ||||||
|  |       </a-form-item> | ||||||
|  |       <a-form-item label="sql数据源" name="original_sql"> | ||||||
|  |         <a-input | ||||||
|  |           placeholder="请输入sql数据源" | ||||||
|  |           v-model="formData.original_sql" | ||||||
|  |         /> | ||||||
|  |       </a-form-item> | ||||||
|  |     </a-form> | ||||||
|  |   </a-modal> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script setup> | ||||||
|  | import { ref, watch } from "vue"; | ||||||
|  | 
 | ||||||
|  | const props = defineProps({ | ||||||
|  |   type: { | ||||||
|  |     type: String, | ||||||
|  |     default: "add", | ||||||
|  |   }, | ||||||
|  |   data: { | ||||||
|  |     type: Object, | ||||||
|  |     default: () => ({}), | ||||||
|  |   }, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const emit = defineEmits(["ok"]); | ||||||
|  | const formRules = { | ||||||
|  |   field_title: [ | ||||||
|  |     { required: true, message: "请输入字段标题", trigger: "submit" }, | ||||||
|  |   ], | ||||||
|  |   field_name: [ | ||||||
|  |     { required: true, message: "请输入字段名称", trigger: "submit" }, | ||||||
|  |   ], | ||||||
|  |   field_type_id: [ | ||||||
|  |     { required: true, message: "请选择字段类型", trigger: "submit" }, | ||||||
|  |   ], | ||||||
|  |   belong_to_table: [ | ||||||
|  |     { required: true, message: "请输入所属表", trigger: "submit" }, | ||||||
|  |   ], | ||||||
|  |   original_sql: [ | ||||||
|  |     { required: true, message: "请输入sql数据源", trigger: "submit" }, | ||||||
|  |   ], | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const formRef = ref(); | ||||||
|  | const formData = ref({ | ||||||
|  |   field_title: undefined, | ||||||
|  |   field_name: undefined, | ||||||
|  |   is_search: 0, | ||||||
|  |   field_type_id: undefined, | ||||||
|  |   belong_to_table: undefined, | ||||||
|  |   original_sql: undefined, | ||||||
|  |   modular_id: undefined, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | watch( | ||||||
|  |   () => props.type, | ||||||
|  |   (newVal) => { | ||||||
|  |     if (newVal === "add") { | ||||||
|  |       resetFormData(); | ||||||
|  |     } else { | ||||||
|  |       formData.value = props.data; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | const resetFormData = () => {}; | ||||||
|  | 
 | ||||||
|  | const handleOk = () => { | ||||||
|  |   formRef.value.validate().then(() => { | ||||||
|  |     emit("ok", formData.value); | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  | </script> | ||||||
							
								
								
									
										10
									
								
								src/views/config-manage/view-cfg/config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/views/config-manage/view-cfg/config.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | |||||||
|  | export const viewCfgCols = [ | ||||||
|  |   { dataIndex: 'field_name', title: '字段名称', align: 'center'}, | ||||||
|  |   { dataIndex: 'field_title', title: '字段标题', align: 'center'}, | ||||||
|  |   { dataIndex: 'field_type_name', title: '字段类型', align: 'center'}, | ||||||
|  |   { dataIndex: 'is_search', title: '是否可搜索', align: 'center'}, | ||||||
|  |   { dataIndex: 'sort', title: '排序', align: 'center'}, | ||||||
|  |   { dataIndex: 'belong_to_table', title: '所属表名称', align: 'center'}, | ||||||
|  |   { dataIndex: 'original_sql', title: 'sql数据源', align: 'center'}, | ||||||
|  |   { dataIndex: 'action', title: '操作', align: 'center'}, | ||||||
|  | ]; | ||||||
							
								
								
									
										181
									
								
								src/views/config-manage/view-cfg/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										181
									
								
								src/views/config-manage/view-cfg/index.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,181 @@ | |||||||
|  | <template> | ||||||
|  |   <div class="normal-container view-cfg-box"> | ||||||
|  |     <div class="header-box"> | ||||||
|  |       <a-space> | ||||||
|  |         <a-input | ||||||
|  |           v-model:value="fieldName" | ||||||
|  |           placeholder="请输入字段名称" | ||||||
|  |           allow-clear | ||||||
|  |           style="width: 200px" | ||||||
|  |           @change="search" | ||||||
|  |         /> | ||||||
|  |         <a-select | ||||||
|  |           placeholder="请选择数据表id" | ||||||
|  |           v-model:value="modularId" | ||||||
|  |           allow-clear | ||||||
|  |           style="width: 160px" | ||||||
|  |           @change="search" | ||||||
|  |         ></a-select> | ||||||
|  |         <a-button type="primary" @click="openCreateModal">新建</a-button> | ||||||
|  |       </a-space> | ||||||
|  |     </div> | ||||||
|  |     <div class="content-box"> | ||||||
|  |       <a-table | ||||||
|  |         :columns="viewCfgCols" | ||||||
|  |         :data-source="dataList" | ||||||
|  |         :pagination="false" | ||||||
|  |         size="small" | ||||||
|  |         bordered | ||||||
|  |       > | ||||||
|  |         <template #bodyCell="{ column, record }"> | ||||||
|  |           <template v-if="column.dataIndex === 'action'"> | ||||||
|  |             <a-space> | ||||||
|  |               <a-button | ||||||
|  |                 type="link" | ||||||
|  |                 size="small" | ||||||
|  |                 @click="toGetDetail(record.field_id)" | ||||||
|  |                 >编辑</a-button | ||||||
|  |               > | ||||||
|  |               <a-popconfirm | ||||||
|  |                 title="确定删除?" | ||||||
|  |                 @confirm="toDelete(record.field_id)" | ||||||
|  |               > | ||||||
|  |                 <a-button type="link" size="small">删除</a-button> | ||||||
|  |               </a-popconfirm> | ||||||
|  |             </a-space> | ||||||
|  |           </template> | ||||||
|  |         </template> | ||||||
|  |       </a-table> | ||||||
|  |       <a-pagination | ||||||
|  |         v-model:current="pageState.page" | ||||||
|  |         :total="pageState.total" | ||||||
|  |         :page-size="pageState.perPage" | ||||||
|  |         :hide-on-single-page="false" | ||||||
|  |         size="small" | ||||||
|  |         class="pagination-box" | ||||||
|  |         @change="toGetFieldList" | ||||||
|  |       /> | ||||||
|  |     </div> | ||||||
|  |     <CreateModal | ||||||
|  |       :width="700" | ||||||
|  |       :open="modalState.visible" | ||||||
|  |       :title="modalState.title" | ||||||
|  |       :type="modalState.type" | ||||||
|  |       :data="modalState.data" | ||||||
|  |       @cancel="modalState.visible = false" | ||||||
|  |       @ok="toSave" | ||||||
|  |     /> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script setup> | ||||||
|  | import { onMounted, ref, reactive } from "vue"; | ||||||
|  | import { viewCfgCols } from "./config"; | ||||||
|  | import { | ||||||
|  |   getFieldList, | ||||||
|  |   deleteField, | ||||||
|  |   saveField, | ||||||
|  |   getFieldDetail, | ||||||
|  | } from "./service"; | ||||||
|  | import CreateModal from "./components/create-modal.vue"; | ||||||
|  | import { message } from "ant-design-vue"; | ||||||
|  | 
 | ||||||
|  | const listLoading = ref(false); | ||||||
|  | const saveLoading = ref(false); | ||||||
|  | const dataList = ref([]); | ||||||
|  | const fieldName = ref(""); | ||||||
|  | const modularId = ref(""); | ||||||
|  | const modalState = reactive({ | ||||||
|  |   title: undefined, | ||||||
|  |   visible: false, | ||||||
|  |   type: "", // add - 新建,edit - 编辑 | ||||||
|  |   data: {}, | ||||||
|  | }); | ||||||
|  | const pageState = reactive({ | ||||||
|  |   page: 1, | ||||||
|  |   perPage: 20, | ||||||
|  |   total: 0, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | onMounted(() => { | ||||||
|  |   toGetFieldList(); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const toGetFieldList = () => { | ||||||
|  |   listLoading.value = true; | ||||||
|  |   getFieldList({ | ||||||
|  |     fieldName: fieldName.value, | ||||||
|  |     modularId: modularId.value, | ||||||
|  |     page: pageState.page, | ||||||
|  |     perPage: pageState.perPage, | ||||||
|  |   }) | ||||||
|  |     .then((res) => { | ||||||
|  |       dataList.value = res.data.list; | ||||||
|  |       pageState.total = res.data.total; | ||||||
|  |     }) | ||||||
|  |     .finally(() => { | ||||||
|  |       listLoading.value = false; | ||||||
|  |     }); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 保存 | ||||||
|  | const toSave = (data) => { | ||||||
|  |   saveLoading.value = true; | ||||||
|  |   saveField(data) | ||||||
|  |     .then(() => { | ||||||
|  |       message.success("保存成功"); | ||||||
|  |     }) | ||||||
|  |     .finally(() => { | ||||||
|  |       saveLoading.value = false; | ||||||
|  |     }); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 获取详情 | ||||||
|  | const toGetDetail = (fieldId) => { | ||||||
|  |   modalState.visible = true; | ||||||
|  |   modalState.title = "编辑"; | ||||||
|  |   modalState.type = "edit"; | ||||||
|  |   getFieldDetail({ fieldId }).then((res) => { | ||||||
|  |     modalState.data = res.data; | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 删除 | ||||||
|  | const toDelete = (fieldId) => { | ||||||
|  |   deleteField({ field_id: fieldId }).then(() => { | ||||||
|  |     message.success("删除成功"); | ||||||
|  |     search(); | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 点击新建 | ||||||
|  | const openCreateModal = () => { | ||||||
|  |   modalState.visible = true; | ||||||
|  |   modalState.title = "新建"; | ||||||
|  |   modalState.type = "add"; | ||||||
|  |   modalState.data = {}; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const handleEdit = (record) => { | ||||||
|  |   console.log(record); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const search = () => { | ||||||
|  |   pageState.page = 1; | ||||||
|  |   toGetFieldList(); | ||||||
|  | }; | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .header-box { | ||||||
|  |   margin-bottom: 10px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ant-table-wrapper { | ||||||
|  |   margin-bottom: 10px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .pagination-box { | ||||||
|  |   text-align: center; | ||||||
|  | } | ||||||
|  | </style> | ||||||
							
								
								
									
										40
									
								
								src/views/config-manage/view-cfg/service.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/views/config-manage/view-cfg/service.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,40 @@ | |||||||
|  | import { get, post } from "@/utils/request"; | ||||||
|  | 
 | ||||||
|  | // 获取列表
 | ||||||
|  | export function getFieldList({ modularId, fieldName, page, perPage }) { | ||||||
|  |   return get({ | ||||||
|  |     url: "/api/v1/field/list", | ||||||
|  |     params: { | ||||||
|  |       modularId, | ||||||
|  |       fieldName, | ||||||
|  |       page, | ||||||
|  |       perPage, | ||||||
|  |     }, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 获取详情
 | ||||||
|  | export function getFieldDetail({ fieldId }) { | ||||||
|  |   return get({ | ||||||
|  |     url: "/api/v1/field/info", | ||||||
|  |     params: { | ||||||
|  |       fieldId, | ||||||
|  |     }, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 保存字段
 | ||||||
|  | export function saveField(data) { | ||||||
|  |   return post({ | ||||||
|  |     url: "/api/v1/field/save", | ||||||
|  |     data, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 删除字段
 | ||||||
|  | export function deleteField(data) { | ||||||
|  |   return post({ | ||||||
|  |     url: "/api/v1/field/del", | ||||||
|  |     data, | ||||||
|  |   }); | ||||||
|  | } | ||||||
| @ -1,3 +0,0 @@ | |||||||
| <template> |  | ||||||
|   data |  | ||||||
| </template> |  | ||||||
| @ -1,3 +0,0 @@ | |||||||
| <template> |  | ||||||
|   create |  | ||||||
| </template> |  | ||||||
| @ -1,8 +0,0 @@ | |||||||
| <template> |  | ||||||
|   <div class="normal-container"> |  | ||||||
|     111 |  | ||||||
|   </div> |  | ||||||
|   <div class="normal-container mt-16"> |  | ||||||
|     222 |  | ||||||
|   </div> |  | ||||||
| </template> |  | ||||||
							
								
								
									
										304
									
								
								src/views/view-all-manage/create-view/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										304
									
								
								src/views/view-all-manage/create-view/index.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,304 @@ | |||||||
|  | <template> | ||||||
|  |   <div class="normal-container"> | ||||||
|  |     <div class="view-create-box"> | ||||||
|  |       <div class="left-box"> | ||||||
|  |         <a-form :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }"> | ||||||
|  |           <a-form-item label="项目" | ||||||
|  |             ><a-select | ||||||
|  |               placeholder="请选择项目" | ||||||
|  |               :options="projectSel" | ||||||
|  |               v-model:value="projectId" | ||||||
|  |               @change="onProjectChange" | ||||||
|  |             ></a-select | ||||||
|  |           ></a-form-item> | ||||||
|  |           <a-form-item label="数据表"> | ||||||
|  |             <a-select | ||||||
|  |               placeholder="请选择" | ||||||
|  |               :options="modularSel" | ||||||
|  |               v-model:value="modularId" | ||||||
|  |               @change="onModularChange" | ||||||
|  |             ></a-select> | ||||||
|  |           </a-form-item> | ||||||
|  |           <a-form-item label="字段"> | ||||||
|  |             <a-checkbox-group v-if="fieldList.length" v-model:value="fieldIds"> | ||||||
|  |               <a-checkbox | ||||||
|  |                 v-for="(item, index) in fieldList" | ||||||
|  |                 :key="index" | ||||||
|  |                 :value="item.value" | ||||||
|  |                 >{{ item.label }}</a-checkbox | ||||||
|  |               > | ||||||
|  |             </a-checkbox-group> | ||||||
|  |             <a-empty v-else description="暂无字段数据" /> | ||||||
|  |           </a-form-item> | ||||||
|  |         </a-form> | ||||||
|  |         <div class="footer"> | ||||||
|  |           <a-button | ||||||
|  |             class="preview-btn" | ||||||
|  |             :loading="previewLoading" | ||||||
|  |             @click="toPreview" | ||||||
|  |             >预览</a-button | ||||||
|  |           > | ||||||
|  |           <a-button type="primary" @click="addViewName">点击保存</a-button> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |       <div class="right-box"> | ||||||
|  |         <div class="y-table-container" v-if="previewData.type === 'table'"> | ||||||
|  |           <div class="y-table-filter"> | ||||||
|  |             <div | ||||||
|  |               v-for="(item, index) in previewData.filterConfig" | ||||||
|  |               :key="item.name" | ||||||
|  |               class="filter-item" | ||||||
|  |             > | ||||||
|  |               <span>{{ item.label }}:</span> | ||||||
|  |               <a-select | ||||||
|  |                 v-if="item.type === 'select'" | ||||||
|  |                 class="input-item" | ||||||
|  |                 :options="item.options" | ||||||
|  |                 v-model:value="previewData.filterData[item.name]" | ||||||
|  |                 placeholder="请选择" | ||||||
|  |                 allow-clear | ||||||
|  |                 @change="toFilt" | ||||||
|  |               ></a-select> | ||||||
|  |               <a-input | ||||||
|  |                 v-if="item.type === 'text'" | ||||||
|  |                 class="input-item" | ||||||
|  |                 placeholder="请输入" | ||||||
|  |                 allow-clear | ||||||
|  |                 v-model:value="previewData.filterData[item.name]" | ||||||
|  |                 @change="toFilt" | ||||||
|  |               /> | ||||||
|  |             </div> | ||||||
|  |           </div> | ||||||
|  |           <div class="y-table-content"> | ||||||
|  |             <a-table | ||||||
|  |               :columns="previewData.columnConfig" | ||||||
|  |               :data-source="previewData.dataList" | ||||||
|  |               :pagination="false" | ||||||
|  |               :scroll="{ x: 1200 }" | ||||||
|  |               size="small" | ||||||
|  |               bordered | ||||||
|  |             ></a-table> | ||||||
|  |             <a-pagination | ||||||
|  |               v-model:current="previewData.page" | ||||||
|  |               :total="previewData.total" | ||||||
|  |               :page-size="previewData.perPage" | ||||||
|  |               :hide-on-single-page="false" | ||||||
|  |               size="small" | ||||||
|  |               class="pagination-box" | ||||||
|  |               @change="toPreview" | ||||||
|  |             /> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |         <div class="preview-area" v-else> | ||||||
|  |           <div><BarChartOutlined /></div> | ||||||
|  |           <div>预览区</div> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |       <a-modal | ||||||
|  |         :open="nameVisible" | ||||||
|  |         title="保存视图" | ||||||
|  |         :bodyStyle="{ paddingTop: '20px' }" | ||||||
|  |         @cancel="nameVisible = false" | ||||||
|  |         @ok="toSaveView" | ||||||
|  |       > | ||||||
|  |         <a-form :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }"> | ||||||
|  |           <a-form-item label="视图名称"> | ||||||
|  |             <a-input placeholder="请输入视图名称" v-model:value="previewName" /> | ||||||
|  |           </a-form-item> | ||||||
|  |         </a-form> | ||||||
|  |       </a-modal> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script setup> | ||||||
|  | import { onMounted, reactive, ref } from "vue"; | ||||||
|  | import { getProModularField, preview, saveView } from "./service"; | ||||||
|  | import { message } from "ant-design-vue"; | ||||||
|  | import { BarChartOutlined } from "@ant-design/icons-vue"; | ||||||
|  | 
 | ||||||
|  | const projectSel = ref([]); | ||||||
|  | const modularSel = ref([]); | ||||||
|  | const fieldList = ref([]); | ||||||
|  | const projectId = ref(); | ||||||
|  | const modularId = ref(); | ||||||
|  | const fieldIds = ref([]); | ||||||
|  | 
 | ||||||
|  | const previewLoading = ref(false); | ||||||
|  | const nameVisible = ref(false); | ||||||
|  | const previewName = ref(); | ||||||
|  | 
 | ||||||
|  | const previewData = reactive({ | ||||||
|  |   type: "", | ||||||
|  |   filterConfig: [], // 筛选条件 | ||||||
|  |   columnConfig: [], // 表格表头 | ||||||
|  |   dataList: [], // 表格数据 | ||||||
|  |   filterData: {}, | ||||||
|  |   page: 1, | ||||||
|  |   perPage: 20, | ||||||
|  |   total: 0, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | onMounted(() => { | ||||||
|  |   toGetProModularField(); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const toGetProModularField = () => { | ||||||
|  |   getProModularField().then((res) => { | ||||||
|  |     projectSel.value = res.data; | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const onProjectChange = (val) => { | ||||||
|  |   const target = projectSel.value.find((item) => item.value === val); | ||||||
|  |   modularSel.value = target.child; | ||||||
|  |   modularId.value = undefined; | ||||||
|  |   fieldList.value = []; | ||||||
|  |   fieldIds.value = []; | ||||||
|  |   resetPreviewData(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const onModularChange = (val) => { | ||||||
|  |   const target = modularSel.value.find((item) => item.value === val); | ||||||
|  |   fieldList.value = target.child; | ||||||
|  |   resetPreviewData(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const resetPreviewData = () => { | ||||||
|  |   previewData.type = ""; | ||||||
|  |   previewData.filterConfig = []; | ||||||
|  |   previewData.columnConfig = []; | ||||||
|  |   previewData.dataList = []; | ||||||
|  |   previewData.filterData = {}; | ||||||
|  |   previewData.page = 1; | ||||||
|  |   previewData.perPage = 20; | ||||||
|  |   previewData.total = 0; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const toPreview = () => { | ||||||
|  |   previewLoading.value = true; | ||||||
|  |   const filter = previewData.filterConfig | ||||||
|  |     .filter((item) => { | ||||||
|  |       return previewData.filterData[item.name] !== undefined; | ||||||
|  |     }) | ||||||
|  |     .map((item) => { | ||||||
|  |       return { | ||||||
|  |         name: item.name, | ||||||
|  |         type: item.type, | ||||||
|  |         value: previewData.filterData[item.name], | ||||||
|  |       }; | ||||||
|  |     }); | ||||||
|  |   preview({ | ||||||
|  |     modularId: modularId.value, | ||||||
|  |     fieldIds: fieldIds.value.toString(), | ||||||
|  |     page: previewData.page, | ||||||
|  |     perPage: previewData.perPage, | ||||||
|  |     filter, | ||||||
|  |   }) | ||||||
|  |     .then((res) => { | ||||||
|  |       previewData.type = res.data.type; | ||||||
|  |       if (res.data.type === "table") { | ||||||
|  |         previewData.filterConfig = res.data.filter; | ||||||
|  |         previewData.filterConfig.forEach((item) => { | ||||||
|  |           previewData.filterData[item.name] = previewData.filterData[item.name] | ||||||
|  |             ? previewData.filterData[item.name] | ||||||
|  |             : undefined; | ||||||
|  |         }); | ||||||
|  |         previewData.columnConfig = res.data.header; | ||||||
|  |         previewData.dataList = res.data.data; | ||||||
|  |         previewData.total = res.data.count; | ||||||
|  |       } | ||||||
|  |     }) | ||||||
|  |     .finally(() => { | ||||||
|  |       previewLoading.value = false; | ||||||
|  |     }); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const addViewName = () => { | ||||||
|  |   nameVisible.value = true; | ||||||
|  |   previewName.value = null; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const toSaveView = () => { | ||||||
|  |   if (!previewName.value) { | ||||||
|  |     message.error("请输入名称"); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |   saveView({ | ||||||
|  |     modularId: modularId.value, | ||||||
|  |     fieldIds: fieldIds.value.toString(), | ||||||
|  |     previewName: previewName.value, | ||||||
|  |   }).then(() => { | ||||||
|  |     message.success("保存成功"); | ||||||
|  |     nameVisible.value = false; | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const toFilt = () => { | ||||||
|  |   previewData.page = 1; | ||||||
|  |   toPreview(); | ||||||
|  | }; | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .normal-container { | ||||||
|  |   height: calc(100vh - 120px); | ||||||
|  |   padding: 16px; | ||||||
|  | } | ||||||
|  | .view-create-box { | ||||||
|  |   display: flex; | ||||||
|  |   height: 100%; | ||||||
|  | } | ||||||
|  | .left-box { | ||||||
|  |   width: 320px; | ||||||
|  |   flex-shrink: 0; | ||||||
|  |   height: calc(100% - 20px); | ||||||
|  |   padding: 10px; | ||||||
|  |   border-right: 1px solid #ddd; | ||||||
|  | 
 | ||||||
|  |   .footer { | ||||||
|  |     text-align: right; | ||||||
|  |     .preview-btn { | ||||||
|  |       margin-right: 10px; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | .right-box { | ||||||
|  |   padding: 0 0 0 10px; | ||||||
|  |   flex-grow: 1; | ||||||
|  |   overflow: auto; | ||||||
|  | } | ||||||
|  | .preview-area { | ||||||
|  |   background-color: #f6f6f6; | ||||||
|  |   height: 100%; | ||||||
|  |   width: 100%; | ||||||
|  |   font-size: 20px; | ||||||
|  |   color: #999; | ||||||
|  |   display: flex; | ||||||
|  |   flex-direction: column; | ||||||
|  |   justify-content: center; | ||||||
|  |   align-items: center; | ||||||
|  |   .anticon-bar-chart { | ||||||
|  |     font-size: 100px; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .y-table-filter { | ||||||
|  |   display: flex; | ||||||
|  |   flex-wrap: wrap; | ||||||
|  | } | ||||||
|  | .filter-item { | ||||||
|  |   margin-right: 10px; | ||||||
|  |   margin-bottom: 6px; | ||||||
|  | } | ||||||
|  | .input-item { | ||||||
|  |   width: 180px; | ||||||
|  | } | ||||||
|  | .y-table-content { | ||||||
|  |   margin-top: 10px; | ||||||
|  | } | ||||||
|  | .pagination-box { | ||||||
|  |   text-align: center; | ||||||
|  |   margin-top: 10px; | ||||||
|  | } | ||||||
|  | </style> | ||||||
							
								
								
									
										34
									
								
								src/views/view-all-manage/create-view/service.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/views/view-all-manage/create-view/service.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | |||||||
|  | import { get, post } from "@/utils/request"; | ||||||
|  | 
 | ||||||
|  | // 项目-表-字段下拉
 | ||||||
|  | export function getProModularField() { | ||||||
|  |   return get({ | ||||||
|  |     url: "/api/v1/field/get-project-modular-field-drop", | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 预览
 | ||||||
|  | export function preview({ modularId, fieldIds, page, perPage, filter }) { | ||||||
|  |   return post({ | ||||||
|  |     url: "api/v1/preview/view", | ||||||
|  |     data: { | ||||||
|  |       modular_id: modularId, | ||||||
|  |       field_ids: fieldIds, | ||||||
|  |       page, | ||||||
|  |       per_page: perPage, | ||||||
|  |       filter, | ||||||
|  |     }, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 点击保存
 | ||||||
|  | export function saveView({ modularId, fieldIds, previewName }) { | ||||||
|  |   return post({ | ||||||
|  |     url: "api/v1/preview/save", | ||||||
|  |     data: { | ||||||
|  |       modular_id: modularId, | ||||||
|  |       field_ids: fieldIds, | ||||||
|  |       preview_name: previewName, | ||||||
|  |     }, | ||||||
|  |   }); | ||||||
|  | } | ||||||
							
								
								
									
										5
									
								
								src/views/view-all-manage/view-list/config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/views/view-all-manage/view-list/config.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | |||||||
|  | export const viewListCols = [ | ||||||
|  |   { dataIndex: 'preview_name', title: '视图名称', align: 'center' }, | ||||||
|  |   { dataIndex: 'created_at', title: '创建时间', align: 'center' }, | ||||||
|  |   { dataIndex: 'action', title: '操作', align: 'center' }, | ||||||
|  | ]; | ||||||
							
								
								
									
										220
									
								
								src/views/view-all-manage/view-list/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										220
									
								
								src/views/view-all-manage/view-list/index.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,220 @@ | |||||||
|  | <template> | ||||||
|  |   <div class="normal-container"> | ||||||
|  |     <div class="view-list-box"> | ||||||
|  |       <div class="left-box"> | ||||||
|  |         <a-form :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }"> | ||||||
|  |           <a-form-item label="项目"> | ||||||
|  |             <a-select | ||||||
|  |               :options="projectSel" | ||||||
|  |               v-model:value="projectId" | ||||||
|  |               placeholder="请选择项目" | ||||||
|  |               @change="onProjectChange" | ||||||
|  |             ></a-select> | ||||||
|  |           </a-form-item> | ||||||
|  |           <a-form-item label="数据表"> | ||||||
|  |             <a-select | ||||||
|  |               :options="modularSel" | ||||||
|  |               v-model:value="modularId" | ||||||
|  |               placeholder="请选择数据表" | ||||||
|  |               @change="onModularChange" | ||||||
|  |             ></a-select> | ||||||
|  |           </a-form-item> | ||||||
|  |         </a-form> | ||||||
|  |         <a-table | ||||||
|  |           :data-source="dataList" | ||||||
|  |           :columns="viewListCols" | ||||||
|  |           :pagination="false" | ||||||
|  |           :row-class-name=" | ||||||
|  |             (record) => | ||||||
|  |               record.preview_id === selectedRowId ? 'selected-row' : '' | ||||||
|  |           " | ||||||
|  |           :custom-row=" | ||||||
|  |             (record, index) => { | ||||||
|  |               return { | ||||||
|  |                 onClick: () => { | ||||||
|  |                   selectedRowId = record.preview_id; | ||||||
|  |                   toGetViewInfo(); | ||||||
|  |                 }, | ||||||
|  |               }; | ||||||
|  |             } | ||||||
|  |           " | ||||||
|  |           size="small" | ||||||
|  |           bordered | ||||||
|  |         > | ||||||
|  |           <template #bodyCell="{ column, record }"> | ||||||
|  |             <template v-if="column.dataIndex === 'action'"> | ||||||
|  |               <a-popconfirm | ||||||
|  |                 title="确定删除?" | ||||||
|  |                 @confirm="toDelete(record.preview_id)" | ||||||
|  |               > | ||||||
|  |                 <a-button | ||||||
|  |                   type="link" | ||||||
|  |                   @click=" | ||||||
|  |                     (e) => { | ||||||
|  |                       e.stopPropagation(); | ||||||
|  |                     } | ||||||
|  |                   " | ||||||
|  |                   >删除</a-button | ||||||
|  |                 > | ||||||
|  |               </a-popconfirm> | ||||||
|  |             </template> | ||||||
|  |           </template> | ||||||
|  |         </a-table> | ||||||
|  |         <a-pagination | ||||||
|  |           v-model:current="pageState.page" | ||||||
|  |           :total="pageState.total" | ||||||
|  |           :page-size="pageState.perPage" | ||||||
|  |           :hide-on-single-page="false" | ||||||
|  |           size="small" | ||||||
|  |           class="pagination-box" | ||||||
|  |           @change="toGetList" | ||||||
|  |         /> | ||||||
|  |       </div> | ||||||
|  |       <div class="right-box"> | ||||||
|  |         <y-table | ||||||
|  |           v-if="selectViewInfo.type === 'table'" | ||||||
|  |           :filterConfig="selectViewInfo.filter" | ||||||
|  |           :dataList="selectViewInfo.data" | ||||||
|  |           :columnConfig="selectViewInfo.header" | ||||||
|  |           :total="selectViewInfo.count" | ||||||
|  |           @toFilt=" | ||||||
|  |             (params) => { | ||||||
|  |               toGetViewInfo(params); | ||||||
|  |             } | ||||||
|  |           " | ||||||
|  |         /> | ||||||
|  |         <div class="preview-area" v-else> | ||||||
|  |           <div><BarChartOutlined /></div> | ||||||
|  |           <div>展示区</div> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script setup> | ||||||
|  | import { onMounted, ref, reactive } from "vue"; | ||||||
|  | import { getProModular, getViewList, getViewInfo, deleteView } from "./service"; | ||||||
|  | import { viewListCols } from "./config"; | ||||||
|  | import yTable from "@/components/common/y-table.vue"; | ||||||
|  | import { message } from "ant-design-vue"; | ||||||
|  | import { BarChartOutlined } from "@ant-design/icons-vue"; | ||||||
|  | 
 | ||||||
|  | const projectSel = ref([]); | ||||||
|  | const modularSel = ref([]); | ||||||
|  | const projectId = ref(); | ||||||
|  | const modularId = ref(); | ||||||
|  | const dataList = ref([]); | ||||||
|  | const selectedRowId = ref(); | ||||||
|  | const selectViewInfo = ref({ | ||||||
|  |   type: "", | ||||||
|  |   filter: [], | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const pageState = reactive({ | ||||||
|  |   page: 1, | ||||||
|  |   perPage: 20, | ||||||
|  |   total: 0, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | onMounted(() => { | ||||||
|  |   toGetProModular(); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const toGetProModular = () => { | ||||||
|  |   getProModular().then((res) => { | ||||||
|  |     projectSel.value = res.data; | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const toGetList = () => { | ||||||
|  |   getViewList({ | ||||||
|  |     modularId: modularId.value, | ||||||
|  |     page: pageState.page, | ||||||
|  |     perPage: pageState.perPage, | ||||||
|  |   }).then((res) => { | ||||||
|  |     dataList.value = res.data.list; | ||||||
|  |     pageState.total = res.data.total; | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const toGetViewInfo = (params = {}) => { | ||||||
|  |   getViewInfo({ | ||||||
|  |     previewId: selectedRowId.value, | ||||||
|  |     ...params, | ||||||
|  |   }).then((res) => { | ||||||
|  |     selectViewInfo.value = res.data; | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const onProjectChange = (val) => { | ||||||
|  |   modularSel.value = projectSel.value.find((item) => item.value === val).child; | ||||||
|  |   modularId.value = null; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const onModularChange = () => { | ||||||
|  |   pageState.page = 1; | ||||||
|  |   toGetList(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const toDelete = (previewId) => { | ||||||
|  |   deleteView({ previewId }).then(() => { | ||||||
|  |     message.success("删除成功"); | ||||||
|  |     toGetList(); | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .normal-container { | ||||||
|  |   height: calc(100vh - 120px); | ||||||
|  |   padding: 16px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .view-list-box { | ||||||
|  |   display: flex; | ||||||
|  |   height: 100%; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .left-box { | ||||||
|  |   width: 320px; | ||||||
|  |   height: calc(100% - 20px); | ||||||
|  |   padding: 10px; | ||||||
|  |   flex-shrink: 0; | ||||||
|  |   border-right: 1px solid #ddd; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | :deep(.ant-table-row:hover) { | ||||||
|  |   cursor: pointer; | ||||||
|  |   background-color: #e6f4ff; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | :deep(.selected-row) { | ||||||
|  |   background-color: #e6f4ff; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .pagination-box { | ||||||
|  |   text-align: center; | ||||||
|  |   margin-top: 10px; | ||||||
|  | } | ||||||
|  | .right-box { | ||||||
|  |   padding: 0 0 0 10px; | ||||||
|  |   flex-grow: 1; | ||||||
|  |   overflow: auto; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .preview-area { | ||||||
|  |   background-color: #f6f6f6; | ||||||
|  |   height: 100%; | ||||||
|  |   width: 100%; | ||||||
|  |   font-size: 20px; | ||||||
|  |   color: #999; | ||||||
|  |   display: flex; | ||||||
|  |   flex-direction: column; | ||||||
|  |   justify-content: center; | ||||||
|  |   align-items: center; | ||||||
|  |   .anticon-bar-chart { | ||||||
|  |     font-size: 100px; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
							
								
								
									
										48
									
								
								src/views/view-all-manage/view-list/service.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/views/view-all-manage/view-list/service.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,48 @@ | |||||||
|  | import { get, post } from "@/utils/request"; | ||||||
|  | 
 | ||||||
|  | // 联动下拉
 | ||||||
|  | export function getProModular() { | ||||||
|  |   return get({ | ||||||
|  |     url: "/api/v1/field/get-project-modular-drop", | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 视图列表
 | ||||||
|  | export function getViewList({ modularId, page = 1, perPage = 20 }) { | ||||||
|  |   return get({ | ||||||
|  |     url: "/api/v1/preview/list", | ||||||
|  |     params: { | ||||||
|  |       modular_id: modularId, | ||||||
|  |       page, | ||||||
|  |       perPage: perPage, | ||||||
|  |     }, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 查看视图
 | ||||||
|  | export function getViewInfo({ | ||||||
|  |   previewId, | ||||||
|  |   page = 1, | ||||||
|  |   perPage = 20, | ||||||
|  |   filter = [], | ||||||
|  | }) { | ||||||
|  |   return post({ | ||||||
|  |     url: "/api/v1/preview/info", | ||||||
|  |     data: { | ||||||
|  |       preview_id: previewId, | ||||||
|  |       page, | ||||||
|  |       per_page: perPage, | ||||||
|  |       filter, | ||||||
|  |     }, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 删除视图
 | ||||||
|  | export function deleteView({ previewId }) { | ||||||
|  |   return post({ | ||||||
|  |     url: "/api/v1/preview/del", | ||||||
|  |     data: { | ||||||
|  |       preview_id: previewId, | ||||||
|  |     }, | ||||||
|  |   }); | ||||||
|  | } | ||||||
| @ -4,6 +4,7 @@ import vue from '@vitejs/plugin-vue'; | |||||||
| import vueJsx from '@vitejs/plugin-vue-jsx'; | import vueJsx from '@vitejs/plugin-vue-jsx'; | ||||||
| import Components from 'unplugin-vue-components/vite'; | import Components from 'unplugin-vue-components/vite'; | ||||||
| import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers'; | import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers'; | ||||||
|  | import { server } from 'typescript'; | ||||||
| 
 | 
 | ||||||
| // https://vitejs.dev/config/
 | // https://vitejs.dev/config/
 | ||||||
| export default defineConfig({ | export default defineConfig({ | ||||||
| @ -21,4 +22,8 @@ export default defineConfig({ | |||||||
|       '@': fileURLToPath(new URL('./src', import.meta.url)), |       '@': fileURLToPath(new URL('./src', import.meta.url)), | ||||||
|     }, |     }, | ||||||
|   }, |   }, | ||||||
|  |   server: { | ||||||
|  |     hmr: true, | ||||||
|  |     host: '0.0.0.0', | ||||||
|  |   }, | ||||||
| }); | }); | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 林梓阳
						林梓阳