chore: 容器框架升级,修复项目命令行异常问题
This commit is contained in:
10
apps/y-code-v1/.env.development
Normal file
10
apps/y-code-v1/.env.development
Normal file
@@ -0,0 +1,10 @@
|
||||
# .env.development
|
||||
VITE_NODE_ENV = development
|
||||
|
||||
VITE_PORT = 10013
|
||||
|
||||
VITE_OA_BASEURL = https://oa-pre.shiyue.com
|
||||
|
||||
VITE_YCODE_BASEURL = https://custom-chart-pre-api.shiyue.com
|
||||
|
||||
VITE_YCODE_BASEURL_FRONT = https://custom-chart.shiyue.com
|
||||
8
apps/y-code-v1/.env.production
Normal file
8
apps/y-code-v1/.env.production
Normal file
@@ -0,0 +1,8 @@
|
||||
VITE_NODE_ENV = prod
|
||||
|
||||
VITE_OA_BASEURL = https://oa.shiyuegame.com
|
||||
|
||||
VITE_YCODE_BASEURL = https://custom-chart-api.shiyuegame.com
|
||||
# VITE_YCODE_BASEURL = https://custom-chart-api.shiyuegame.com:19998
|
||||
|
||||
VITE_YCODE_BASEURL_FRONT = https://custom-chart.shiyuegame.com
|
||||
9
apps/y-code-v1/.env.staging
Normal file
9
apps/y-code-v1/.env.staging
Normal file
@@ -0,0 +1,9 @@
|
||||
VITE_NODE_ENV = staging
|
||||
|
||||
VITE_PORT = 10012
|
||||
|
||||
VITE_OA_BASEURL = https://oa-pre.shiyue.com
|
||||
|
||||
VITE_YCODE_BASEURL = https://custom-chart-pre-api.shiyue.com
|
||||
|
||||
VITE_YCODE_BASEURL_FRONT = https://custom-chart.shiyue.com
|
||||
25
apps/y-code-v1/.eslintrc.cjs
Normal file
25
apps/y-code-v1/.eslintrc.cjs
Normal file
@@ -0,0 +1,25 @@
|
||||
/* eslint-env node */
|
||||
require('@rushstack/eslint-patch/modern-module-resolution');
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
'extends': [
|
||||
'plugin:vue/vue3-essential',
|
||||
'eslint:recommended',
|
||||
'@vue/eslint-config-typescript',
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
},
|
||||
rules: {
|
||||
semi: 0,
|
||||
'vue/multi-word-component-names': 0,
|
||||
indent: [
|
||||
2, 2, {
|
||||
SwitchCase: 1,
|
||||
},
|
||||
],
|
||||
'vue/html-indent': 2,
|
||||
'@typescript-eslint/comma-dangle': [2, 'always-multiline'],
|
||||
},
|
||||
};
|
||||
6
apps/y-code-v1/.npmrc
Normal file
6
apps/y-code-v1/.npmrc
Normal file
@@ -0,0 +1,6 @@
|
||||
# 默认使用淘宝镜像源
|
||||
registry=https://registry.npmmirror.com/
|
||||
|
||||
# 公司私有源配置
|
||||
@sy:registry=http://sy-registry.shiyue.com
|
||||
|
||||
17
apps/y-code-v1/.release.config.js
Normal file
17
apps/y-code-v1/.release.config.js
Normal file
@@ -0,0 +1,17 @@
|
||||
module.exports = {
|
||||
branches: ["main", "master"],
|
||||
plugins: [
|
||||
"@semantic-release/commit-analyzer",
|
||||
"@semantic-release/release-notes-generator",
|
||||
"@semantic-release/changelog",
|
||||
// "@semantic-release/npm",
|
||||
[
|
||||
"@semantic-release/git",
|
||||
{
|
||||
assets: ["CHANGELOG.md", "package.json"],
|
||||
message: "chore(release): ${nextRelease.version} [skip ci]",
|
||||
},
|
||||
],
|
||||
// "@semantic-release/github",
|
||||
],
|
||||
};
|
||||
0
apps/y-code-v1/README.md
Normal file
0
apps/y-code-v1/README.md
Normal file
45
apps/y-code-v1/components.d.ts
vendored
Normal file
45
apps/y-code-v1/components.d.ts
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// @ts-nocheck
|
||||
// Generated by unplugin-vue-components
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
export {}
|
||||
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
ABreadcrumb: typeof import('ant-design-vue/es')['Breadcrumb']
|
||||
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']
|
||||
ACol: typeof import('ant-design-vue/es')['Col']
|
||||
AConfigProvider: typeof import('ant-design-vue/es')['ConfigProvider']
|
||||
ADropdown: typeof import('ant-design-vue/es')['Dropdown']
|
||||
AFloatButton: typeof import('ant-design-vue/es')['FloatButton']
|
||||
AForm: typeof import('ant-design-vue/es')['Form']
|
||||
AFormItem: typeof import('ant-design-vue/es')['FormItem']
|
||||
AImage: typeof import('ant-design-vue/es')['Image']
|
||||
AInput: typeof import('ant-design-vue/es')['Input']
|
||||
AInputNumber: typeof import('ant-design-vue/es')['InputNumber']
|
||||
AInputPassword: typeof import('ant-design-vue/es')['InputPassword']
|
||||
AMenu: typeof import('ant-design-vue/es')['Menu']
|
||||
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']
|
||||
ARadio: typeof import('ant-design-vue/es')['Radio']
|
||||
ARadioButton: typeof import('ant-design-vue/es')['RadioButton']
|
||||
ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup']
|
||||
ARangePicker: typeof import('ant-design-vue/es')['RangePicker']
|
||||
ARow: typeof import('ant-design-vue/es')['Row']
|
||||
ASelect: typeof import('ant-design-vue/es')['Select']
|
||||
ASpace: typeof import('ant-design-vue/es')['Space']
|
||||
ASpin: typeof import('ant-design-vue/es')['Spin']
|
||||
ASwitch: typeof import('ant-design-vue/es')['Switch']
|
||||
ATable: typeof import('ant-design-vue/es')['Table']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
YChart: typeof import('./src/components/common/y-chart.vue')['default']
|
||||
YTable: typeof import('./src/components/common/y-table.vue')['default']
|
||||
}
|
||||
}
|
||||
1
apps/y-code-v1/env.d.ts
vendored
Normal file
1
apps/y-code-v1/env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
50
apps/y-code-v1/farm.config.ts
Normal file
50
apps/y-code-v1/farm.config.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import path from 'node:path';
|
||||
|
||||
import { defineConfig, loadEnv } from '@farmfe/core';
|
||||
import less from '@farmfe/js-plugin-less';
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
import vueJsx from '@vitejs/plugin-vue-jsx';
|
||||
import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers';
|
||||
import Components from 'unplugin-vue-components/vite';
|
||||
import mkcert from 'vite-plugin-mkcert';
|
||||
import qiankun from 'vite-plugin-qiankun';
|
||||
|
||||
// @ts-ignore
|
||||
export default defineConfig(({ mode }) => {
|
||||
console.log('mode', mode);
|
||||
const env = loadEnv(mode, process.cwd(), ['VITE_']);
|
||||
return {
|
||||
plugins: [less()],
|
||||
vitePlugins: [
|
||||
vue(),
|
||||
vueJsx(),
|
||||
mkcert(),
|
||||
qiankun('y-code-app', {
|
||||
useDevMode: env.VITE_NODE_ENV === 'development',
|
||||
}) as any,
|
||||
Components({
|
||||
resolvers: [
|
||||
AntDesignVueResolver({
|
||||
importStyle: 'less',
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
server: {
|
||||
cors: true,
|
||||
port: Number(env.VITE_PORT),
|
||||
},
|
||||
|
||||
compilation: {
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, './src'),
|
||||
},
|
||||
},
|
||||
// output: {
|
||||
// path: path.resolve(process.cwd(), "../../dist/y-code-v1"),
|
||||
// clean: true,
|
||||
// },
|
||||
},
|
||||
};
|
||||
});
|
||||
13
apps/y-code-v1/index.html
Normal file
13
apps/y-code-v1/index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>悦码后台</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="y-code-app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
55
apps/y-code-v1/package.json
Normal file
55
apps/y-code-v1/package.json
Normal file
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"name": "@sy/low-code-y-code-v1",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "cross-env farm start --mode development",
|
||||
"build": "cross-env farm build --mode production",
|
||||
"build:staging": "cross-env farm build --mode staging",
|
||||
"preview": "cross-env farm preview",
|
||||
"clean": "rimraf node_modules"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/icons-vue": "^7.0.1",
|
||||
"@antv/g2plot": "^2.4.31",
|
||||
"@farmfe/js-plugin-less": "^1.12.1",
|
||||
"@sy/y-code-chart": "^1.2.1",
|
||||
"@vueuse/core": "^10.11.0",
|
||||
"ant-design-vue": "^4.2.6",
|
||||
"axios": "catalog:",
|
||||
"core-js": "^3.40.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"dayjs": "catalog:",
|
||||
"lodash-es": "^4.17.21",
|
||||
"p-limit": "^6.1.0",
|
||||
"pinia": "catalog:",
|
||||
"vue": "catalog:",
|
||||
"vue-grid-layout": "^3.0.0-beta1",
|
||||
"vue-router": "catalog:",
|
||||
"wujie-vue3": "^1.0.25"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@farmfe/cli": "^1.0.4",
|
||||
"@farmfe/core": "^1.6.6",
|
||||
"@rushstack/eslint-patch": "^1.10.5",
|
||||
"@tsconfig/node20": "^20.1.4",
|
||||
"@types/node": "^20.17.17",
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"@vitejs/plugin-vue-jsx": "^4.1.1",
|
||||
"@vue/eslint-config-typescript": "^13.0.0",
|
||||
"@vue/tsconfig": "^0.7.0",
|
||||
"eslint": "^9.20.1",
|
||||
"eslint-plugin-vue": "^9.32.0",
|
||||
"less": "^4.2.2",
|
||||
"semantic-release": "^24.2.2",
|
||||
"typescript": "catalog:",
|
||||
"unplugin-vue-components": "^0.26.0",
|
||||
"vite": "catalog:",
|
||||
"vite-plugin-mkcert": "catalog:",
|
||||
"vite-plugin-qiankun": "^1.0.15",
|
||||
"vue-tsc": "catalog:",
|
||||
"yargs-parser": "^21.1.1"
|
||||
},
|
||||
"packageManager": "pnpm@10.6.2"
|
||||
}
|
||||
BIN
apps/y-code-v1/public/favicon.ico
Normal file
BIN
apps/y-code-v1/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
30
apps/y-code-v1/src/App.vue
Normal file
30
apps/y-code-v1/src/App.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<script setup>
|
||||
import zhCN from 'ant-design-vue/es/locale/zh_CN';
|
||||
import { legacyLogicalPropertiesTransformer, StyleProvider, ConfigProvider, theme } from 'ant-design-vue';
|
||||
import dayjs from 'dayjs';
|
||||
import 'dayjs/locale/zh-cn';
|
||||
|
||||
const { compactAlgorithm } = theme;
|
||||
|
||||
|
||||
dayjs.locale('zh-cn');
|
||||
|
||||
ConfigProvider.config({
|
||||
prefixCls: 'ycode-ant',
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-config-provider
|
||||
:theme="{
|
||||
algorithm: [ compactAlgorithm],
|
||||
}"
|
||||
:locale="zhCN"
|
||||
:transformers="[legacyLogicalPropertiesTransformer]"
|
||||
prefix-cls="ycode-ant"
|
||||
>
|
||||
<StyleProvider hash-priority="high">
|
||||
<router-view />
|
||||
</StyleProvider>
|
||||
</a-config-provider>
|
||||
</template>
|
||||
34
apps/y-code-v1/src/api/common.ts
Normal file
34
apps/y-code-v1/src/api/common.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { get } from "@/utils/request";
|
||||
|
||||
export interface UserInfoType {
|
||||
alias: string;
|
||||
all_dept_name: string;
|
||||
avatar: string;
|
||||
dept: string;
|
||||
dept_id: number;
|
||||
email: string;
|
||||
job: string;
|
||||
job_name: string;
|
||||
job_type: string;
|
||||
mobile: string;
|
||||
user_id: number;
|
||||
username: string;
|
||||
}
|
||||
|
||||
interface DropListItem {
|
||||
label: string;
|
||||
value: string | number;
|
||||
mark: string;
|
||||
}
|
||||
|
||||
export const getUserInfo = () =>
|
||||
get<UserInfoType>({
|
||||
url: "/api/home/grade",
|
||||
});
|
||||
|
||||
export const logout = () => get({ url: "/api/common/logout" });
|
||||
|
||||
export const getProjectDrop = () =>
|
||||
get<DropListItem[]>({
|
||||
url: "/api/v1/project/get-project-drop",
|
||||
});
|
||||
21
apps/y-code-v1/src/api/preview/index.ts
Normal file
21
apps/y-code-v1/src/api/preview/index.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { post } from "@/utils/request";
|
||||
|
||||
interface PreviewItemParams {
|
||||
previewId: string | number;
|
||||
filter?: string | [];
|
||||
page?: number;
|
||||
perPage?: number;
|
||||
}
|
||||
|
||||
// 查看视图
|
||||
export function searchInfo(data: PreviewItemParams) {
|
||||
return post({
|
||||
url: `/api/v1/preview/info`,
|
||||
data: {
|
||||
preview_id: data.previewId,
|
||||
filter: data.filter,
|
||||
page: data.page,
|
||||
per_page: data.perPage,
|
||||
},
|
||||
});
|
||||
}
|
||||
BIN
apps/y-code-v1/src/assets/avatar.png
Normal file
BIN
apps/y-code-v1/src/assets/avatar.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
5
apps/y-code-v1/src/assets/styles/variable.less
Normal file
5
apps/y-code-v1/src/assets/styles/variable.less
Normal file
@@ -0,0 +1,5 @@
|
||||
@primary-color: var(--primary-color); // 主题色
|
||||
@primary-light-color: var(--primary-light-color); // 主题色 - 浅色
|
||||
@table-head-bg-color: var(--table-head-bg-color); // 表头背景色
|
||||
@table-head-font-color: var(--table-head-font-color); // 表头字体颜色
|
||||
@primary-bg-color: #f8f8f8;
|
||||
128
apps/y-code-v1/src/components/common/y-chart.vue
Normal file
128
apps/y-code-v1/src/components/common/y-chart.vue
Normal file
@@ -0,0 +1,128 @@
|
||||
<template>
|
||||
<div class="chart-show-box">
|
||||
<div class="chart-name">
|
||||
<div class="title">{{ title }}</div>
|
||||
</div>
|
||||
<div class="chart-header">
|
||||
<div class="chart-filter">
|
||||
<div
|
||||
v-for="(item, index) in filterConfig"
|
||||
:key="index"
|
||||
class="filter-item"
|
||||
>
|
||||
<div>
|
||||
<a-radio-group v-model:value="dateType" button-style="solid">
|
||||
<a-radio-button value="day">日</a-radio-button>
|
||||
<a-radio-button value="week">周</a-radio-button>
|
||||
<a-radio-button value="month">月</a-radio-button>
|
||||
</a-radio-group>
|
||||
<a-range-picker
|
||||
v-if="item.type === 'time'"
|
||||
class="date-item"
|
||||
v-model:value="filterData[item.name]"
|
||||
:picker="rangePicker"
|
||||
@change="toFilt"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<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'" :config="currentChart" />
|
||||
<Line v-if="chartType === 'line'" :config="currentChart" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref } from "vue";
|
||||
import Line from "@/plugins/antv-g2plot/line.vue";
|
||||
import Column from "@/plugins/antv-g2plot/column.vue";
|
||||
import { cloneDeep } from "lodash-es";
|
||||
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
chartCfg: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
filterConfig: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
const emit = defineEmits(["toFilt"]);
|
||||
|
||||
const chartType = ref("line");
|
||||
const dateType = ref("day");
|
||||
const filterData = ref({});
|
||||
|
||||
const rangePicker = computed(() => {
|
||||
switch(dateType.value) {
|
||||
case 'week':
|
||||
return 'week';
|
||||
case 'month':
|
||||
return 'month';
|
||||
default:
|
||||
return 'date';
|
||||
}
|
||||
});
|
||||
|
||||
const currentChart = computed(() => {
|
||||
return props.chartCfg[chartType.value];
|
||||
})
|
||||
|
||||
const toFilt = () => {
|
||||
const cloneFilter = cloneDeep(props.filterConfig);
|
||||
const filter = cloneFilter
|
||||
.filter((item) => {
|
||||
return filterData.value[item.name] !== undefined && filterData.value[item.name] !== null;
|
||||
})
|
||||
.map((item) => {
|
||||
return item.type === 'time' ? {
|
||||
name: item.name,
|
||||
type: item.type,
|
||||
start_time: filterData.value[item.name][0].format('YYYY-MM-DD'),
|
||||
end_time: filterData.value[item.name][1].format('YYYY-MM-DD'),
|
||||
date_type: dateType.value,
|
||||
} : {
|
||||
name: item.name,
|
||||
type: item.type,
|
||||
value: filterData.value[item.name],
|
||||
}
|
||||
})
|
||||
emit('toFilt', {
|
||||
filter,
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.chart-wrap {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.chart-name {
|
||||
margin-bottom: 8px;
|
||||
.title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.date-item {
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
||||
286
apps/y-code-v1/src/components/common/y-table.vue
Normal file
286
apps/y-code-v1/src/components/common/y-table.vue
Normal file
@@ -0,0 +1,286 @@
|
||||
<template>
|
||||
<div class="y-table-container">
|
||||
<div class="y-table-name">
|
||||
<div class="title">{{ title }}</div>
|
||||
</div>
|
||||
<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"
|
||||
placeholder="请选择"
|
||||
:options="item.options"
|
||||
allow-clear
|
||||
show-search
|
||||
:filter-option="filterOption"
|
||||
v-model:value="filterData[item.name]"
|
||||
@change="toFilt"
|
||||
></a-select>
|
||||
<!-- 输入框 -->
|
||||
<a-input
|
||||
v-else-if="item.type === 'text'"
|
||||
class="input-item"
|
||||
placeholder="请输入"
|
||||
allow-clear
|
||||
v-model:value="filterData[item.name]"
|
||||
@change="toFilt"
|
||||
/>
|
||||
<!-- 日期范围 -->
|
||||
<a-range-picker
|
||||
v-else-if="item.type === 'time'"
|
||||
class="date-item"
|
||||
v-model:value="filterData[item.name]"
|
||||
@change="toFilt"
|
||||
/>
|
||||
<!-- 带时分的日期范围 -->
|
||||
<a-range-picker
|
||||
v-else-if="item.type === 'date_time'"
|
||||
class="date-time-item"
|
||||
v-model:value="filterData[item.name]"
|
||||
show-time
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
@change="toFilt"
|
||||
/>
|
||||
<!-- 数值区间 -->
|
||||
<div
|
||||
v-else-if="item.type === 'number_range' && filterData[item.name]"
|
||||
class="number_range_box"
|
||||
>
|
||||
<a-input-number
|
||||
placeholder="最小值"
|
||||
style="width: 140px"
|
||||
v-model:value="filterData[item.name].min"
|
||||
@change="toFilt"
|
||||
/>
|
||||
<span class="divider"> - </span>
|
||||
<a-input-number
|
||||
placeholder="最大值"
|
||||
style="width: 140px"
|
||||
v-model:value="filterData[item.name].max"
|
||||
@change="toFilt"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="isExport" class="filter-item">
|
||||
<a
|
||||
:href="`${YCODE_BASEURL}/api/v1/preview/export?preview_id=${previewId}&filter=${JSON.stringify(getFilter())}`"
|
||||
target="_blank"
|
||||
><a-button type="primary"><CloudDownloadOutlined />导出</a-button></a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="y-table-content">
|
||||
<a-table
|
||||
:columns="columnConfig"
|
||||
:data-source="dataList"
|
||||
:pagination="false"
|
||||
:scroll="{ x: 1000, y: `calc(100vh - 280px)` }"
|
||||
size="small"
|
||||
bordered
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<a-image
|
||||
v-if="column.show_type === 'img'"
|
||||
:src="record[column.dataIndex]"
|
||||
:width="160"
|
||||
/>
|
||||
<a
|
||||
v-else-if="column.show_type === 'link'"
|
||||
target="_blank"
|
||||
:href="record[column.dataIndex]"
|
||||
>{{ record[column.dataIndex] }}</a
|
||||
>
|
||||
<div
|
||||
v-else-if="column.show_type === 'richText'"
|
||||
v-html="record[column.dataIndex]"
|
||||
></div>
|
||||
<template v-else>{{ record[column.dataIndex] }}</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<a-pagination
|
||||
v-model:current="pageState.page"
|
||||
:total="total"
|
||||
:page-size="pageState.perPage"
|
||||
:hide-on-single-page="false"
|
||||
:show-size-changer="false"
|
||||
size="small"
|
||||
class="pagination-box"
|
||||
:show-total="total => `共 ${total} 条`"
|
||||
@change="pageChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, watch } from "vue";
|
||||
import { useDebounceFn } from "@vueuse/core";
|
||||
import { cloneDeep } from "lodash-es";
|
||||
import { CloudDownloadOutlined } from "@ant-design/icons-vue";
|
||||
|
||||
const YCODE_BASEURL = import.meta.env.VITE_YCODE_BASEURL
|
||||
|
||||
const props = defineProps({
|
||||
previewId: {
|
||||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
filterConfig: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
columnConfig: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
dataList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
total: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
isExport: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
const emit = defineEmits(["toFilt"]);
|
||||
|
||||
const filterData = ref({});
|
||||
|
||||
const pageState = reactive({
|
||||
page: 1,
|
||||
perPage: 20,
|
||||
});
|
||||
|
||||
watch(() => props.filterConfig, (newVal) => {
|
||||
newVal.forEach((item) => {
|
||||
// 给数值区间类型赋初始值,防止报错
|
||||
if (item.type === 'number_range' && !filterData.value[item.name]) {
|
||||
filterData.value[item.name] = {
|
||||
min: undefined,
|
||||
max: undefined,
|
||||
};
|
||||
}
|
||||
});
|
||||
}, { immediate: true });
|
||||
|
||||
const filterOption = (input, option) => {
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||
};
|
||||
|
||||
const getFilter = () => {
|
||||
const cloneFilter = cloneDeep(props.filterConfig);
|
||||
const filter = cloneFilter
|
||||
.filter((item) => {
|
||||
return filterData.value[item.name] !== undefined && filterData.value[item.name] !== null;
|
||||
})
|
||||
.map((item) => {
|
||||
if (item.type === 'time' && filterData.value[item.name]) {
|
||||
// 日期类型的参数
|
||||
return {
|
||||
name: item.name,
|
||||
type: item.type,
|
||||
start_time: filterData.value[item.name][0].format('YYYY-MM-DD'),
|
||||
end_time: filterData.value[item.name][1].format('YYYY-MM-DD'),
|
||||
};
|
||||
} else if (item.type === 'date_time' && filterData.value[item.name]) {
|
||||
// 带时分的日期类型参数
|
||||
return {
|
||||
name: item.name,
|
||||
type: item.type,
|
||||
start_time: filterData.value[item.name][0].format('YYYY-MM-DD HH:mm') + ':00',
|
||||
end_time: filterData.value[item.name][1].format('YYYY-MM-DD HH:mm') + ':59',
|
||||
};
|
||||
} else if (item.type === 'number_range') {
|
||||
// 数值区间
|
||||
return {
|
||||
name: item.name,
|
||||
type: item.type,
|
||||
min: filterData.value[item.name].min ? String(filterData.value[item.name].min) : '',
|
||||
max: filterData.value[item.name].max ? String(filterData.value[item.name].max) : '',
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
name: item.name,
|
||||
type: item.type,
|
||||
value: filterData.value[item.name],
|
||||
};
|
||||
}
|
||||
});
|
||||
return filter
|
||||
};
|
||||
|
||||
const getData = () => {
|
||||
emit("toFilt", {
|
||||
filter: getFilter(),
|
||||
page: pageState.page,
|
||||
perPage: pageState.perPage,
|
||||
});
|
||||
};
|
||||
|
||||
const toFilt = useDebounceFn(() => {
|
||||
pageState.page = 1
|
||||
getData();
|
||||
}, 500);
|
||||
|
||||
const pageChange = () => {
|
||||
getData();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.y-table-name {
|
||||
margin-bottom: 10px;
|
||||
display: none;
|
||||
.title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
.y-table-filter {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.filter-item {
|
||||
margin-right: 10px;
|
||||
margin-bottom: 6px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.input-item {
|
||||
width: 180px;
|
||||
}
|
||||
.date-item {
|
||||
width: 240px;
|
||||
}
|
||||
.date-time-item {
|
||||
width: 300px;
|
||||
}
|
||||
.number_range_box {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
.divider {
|
||||
margin: 0 4px;
|
||||
}
|
||||
}
|
||||
.y-table-content {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.pagination-box {
|
||||
text-align: center;
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
46
apps/y-code-v1/src/global.less
Normal file
46
apps/y-code-v1/src/global.less
Normal file
@@ -0,0 +1,46 @@
|
||||
// @primary-bg-color: #f8f8f8;
|
||||
@import "../src/assets/styles/variable.less";
|
||||
|
||||
html,
|
||||
body {
|
||||
background-color: @primary-bg-color;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
}
|
||||
#app {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 选择滚动条 */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px; /* 滚动条宽度 */
|
||||
}
|
||||
|
||||
/* 滚动条轨道 */
|
||||
::-webkit-scrollbar-track {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
/* 滚动条滑块 */
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(0, 0, 0, 0.15);
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
/* 滚动条滑块悬停 */
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background-color: rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.normal-container {
|
||||
padding: 16px 24px;
|
||||
border-radius: 6px;
|
||||
background-color: #fff;
|
||||
}
|
||||
.mt-8 {
|
||||
margin-top: 8px;
|
||||
}
|
||||
.mt-16 {
|
||||
margin-top: 16px;
|
||||
}
|
||||
93
apps/y-code-v1/src/layout/components/Header.vue
Normal file
93
apps/y-code-v1/src/layout/components/Header.vue
Normal file
@@ -0,0 +1,93 @@
|
||||
<script setup lang="ts">
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import { logout } from '@/api/common';
|
||||
import avatar from '@/assets/avatar.png';
|
||||
import { useUserInfoStore } from '@/stores/useUserInfoStore';
|
||||
import { YCODE_BASEURL } from '@/utils/request';
|
||||
import { FullscreenOutlined, HomeOutlined } from '@ant-design/icons-vue';
|
||||
|
||||
const emits = defineEmits(['requestFullscreen']);
|
||||
|
||||
const route = useRoute();
|
||||
const userInfoStore = useUserInfoStore();
|
||||
|
||||
const handleLogout = () => {
|
||||
logout().then(() => {
|
||||
window.location.href = `${YCODE_BASEURL}/login?redirect=${encodeURIComponent(
|
||||
window.location.href,
|
||||
)}`;
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="root">
|
||||
<a-breadcrumb>
|
||||
<a-breadcrumb-item v-for="item in route.matched" :key="item.path">
|
||||
<HomeOutlined v-if="item.path === '/' && item.name === 'layout'" />
|
||||
<span v-else>{{ item.meta.title || item.path }}</span>
|
||||
</a-breadcrumb-item>
|
||||
</a-breadcrumb>
|
||||
|
||||
<div class="user-area">
|
||||
<div class="fullscreen-icon-area" @click="emits('requestFullscreen')">
|
||||
<FullscreenOutlined class="fullscreen-icon" />
|
||||
</div>
|
||||
<a-dropdown placement="bottom">
|
||||
<div style="display: flex; align-items: center; cursor: pointer">
|
||||
<img :src="userInfoStore.userInfo?.avatar || avatar" class="avatar" />
|
||||
<div>{{ userInfoStore.userInfo?.alias || '-' }}</div>
|
||||
</div>
|
||||
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item @click="handleLogout">
|
||||
<span>退出登录</span>
|
||||
</a-menu-item>
|
||||
<a-menu-item>
|
||||
<a class="back-oa" :href="`${OA_BASEURL}/front/`"> 返回OA </a>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.root {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 30px;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.08);
|
||||
.user-area {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: rgb(51, 51, 51);
|
||||
.fullscreen-icon-area {
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: #ecf4fe;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
.fullscreen-icon {
|
||||
font-size: 20px;
|
||||
color: #1890ff;
|
||||
}
|
||||
}
|
||||
.avatar {
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
border-radius: 50%;
|
||||
margin-left: 16px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
70
apps/y-code-v1/src/layout/components/Sider.vue
Normal file
70
apps/y-code-v1/src/layout/components/Sider.vue
Normal file
@@ -0,0 +1,70 @@
|
||||
<script lang="ts"></script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { RouteType } from '@/router/routes';
|
||||
import type { ItemType } from 'ant-design-vue';
|
||||
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
import routeList from '@/router/routes';
|
||||
|
||||
function formatMemu(list: RouteType[], path: string = ''): ItemType[] {
|
||||
return list
|
||||
.filter((i) => i.isMenu)
|
||||
.map((item) => {
|
||||
const key = item.path.startsWith('/')
|
||||
? item.path
|
||||
: `${path}/${item.path}`;
|
||||
return {
|
||||
key,
|
||||
icon: item.icon,
|
||||
children:
|
||||
item.children.length > 0 ? formatMemu(item.children, key) : void 0,
|
||||
label: item.meta.title || '-',
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
const selectedKeys = ref<string[]>(['1']);
|
||||
const openKeys = ref<string[]>(['sub1']);
|
||||
const menuList = computed(() => formatMemu(routeList[0].children));
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(val) => {
|
||||
if (!selectedKeys.value.includes(val)) {
|
||||
selectedKeys.value = [val];
|
||||
openKeys.value = route.matched.slice(1).map((i) => i.path);
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
const handleClick = (config: { key: string }) => {
|
||||
router.push(config.key);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-menu
|
||||
v-model:open-keys="openKeys"
|
||||
v-model:selected-keys="selectedKeys"
|
||||
mode="inline"
|
||||
:items="menuList"
|
||||
class="sider-root"
|
||||
@click="handleClick"
|
||||
v-bind="$attrs"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.sider-root {
|
||||
flex-grow: 1;
|
||||
overflow-y: auto;
|
||||
border-inline-end: none !important;
|
||||
}
|
||||
</style>
|
||||
136
apps/y-code-v1/src/layout/index.vue
Normal file
136
apps/y-code-v1/src/layout/index.vue
Normal file
@@ -0,0 +1,136 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import Header from "./components/Header.vue";
|
||||
import Sider from "./components/Sider.vue";
|
||||
import {
|
||||
MenuFoldOutlined,
|
||||
MenuUnfoldOutlined,
|
||||
FullscreenExitOutlined,
|
||||
} from "@ant-design/icons-vue";
|
||||
import { useEventListener } from "@vueuse/core";
|
||||
import { qiankunWindow } from 'vite-plugin-qiankun/dist/helper'
|
||||
|
||||
const __POWERED_BY_QIANKUN__ = computed(() => {
|
||||
return qiankunWindow.__POWERED_BY_QIANKUN__ || window?.proxy?.__POWERED_BY_QIANKUN__
|
||||
})
|
||||
|
||||
// const userInfoStore = useUserInfoStore();
|
||||
const isCollapsed = ref(false);
|
||||
const isFullscreen = ref(false);
|
||||
const container = ref<HTMLDivElement>();
|
||||
|
||||
onMounted(() => {
|
||||
// userInfoStore.fetchUserInfo();
|
||||
});
|
||||
|
||||
useEventListener(window, "fullscreenchange", () => {
|
||||
isFullscreen.value = !!document.fullscreenElement;
|
||||
});
|
||||
|
||||
const handleFullscreen = () => {
|
||||
if (container.value) {
|
||||
container.value.requestFullscreen();
|
||||
}
|
||||
};
|
||||
const handleExitFullscreen = () => {
|
||||
document.exitFullscreen?.();
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section v-if="!__POWERED_BY_QIANKUN__" 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 @requestFullscreen="handleFullscreen" />
|
||||
</header>
|
||||
<div class="i-container">
|
||||
<router-view />
|
||||
</div>
|
||||
<a-float-button @click="handleExitFullscreen" v-if="isFullscreen">
|
||||
<template #icon>
|
||||
<FullscreenExitOutlined />
|
||||
</template>
|
||||
</a-float-button>
|
||||
</section>
|
||||
</section>
|
||||
<router-view v-else />
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@header-height: 60px;
|
||||
@aside-width: 220px;
|
||||
@aside-width-collapsed: 60px;
|
||||
@header-margin: 12px;
|
||||
|
||||
.root {
|
||||
height: 100%;
|
||||
.header {
|
||||
height: @header-height;
|
||||
position: fixed;
|
||||
width: calc(100% - 220px);
|
||||
z-index: 1;
|
||||
top: 0;
|
||||
}
|
||||
.left-aside {
|
||||
width: @aside-width;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
height: 100vh;
|
||||
overflow-y: hidden;
|
||||
box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.08);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #fff;
|
||||
transition: width 0.3s cubic-bezier(0.2, 0, 0, 1) 0s;
|
||||
.collapsed-icon {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 8px;
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
.container {
|
||||
padding-left: @aside-width;
|
||||
padding-top: @header-height + @header-margin;
|
||||
height: calc(100% - @header-height - @header-margin);
|
||||
position: relative;
|
||||
background-color: #f8f8f8;
|
||||
transition: padding 0.3s cubic-bezier(0.2, 0, 0, 1) 0s;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.container-collapsed {
|
||||
padding-left: @aside-width-collapsed + 8px;
|
||||
}
|
||||
.container-fullscreen {
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
}
|
||||
:deep(
|
||||
:where(.css-dev-only-do-not-override-1hsjdkk).ant-menu-inline-collapsed
|
||||
),
|
||||
.left-aside-collapsed {
|
||||
width: @aside-width-collapsed;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
63
apps/y-code-v1/src/main.ts
Normal file
63
apps/y-code-v1/src/main.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { createApp } from "vue";
|
||||
import { createPinia } from "pinia";
|
||||
import App from "./App.vue";
|
||||
import { createProjectRouter } from "./router";
|
||||
import "./global.less";
|
||||
import VueGridLayout from "vue-grid-layout"; // 引入layout
|
||||
import {
|
||||
renderWithQiankun,
|
||||
qiankunWindow,
|
||||
} from "vite-plugin-qiankun/dist/helper";
|
||||
|
||||
let app;
|
||||
function render(props: Object = {}) {
|
||||
app = createApp(App);
|
||||
setStyleSheet(props.styles);
|
||||
const router = createProjectRouter(props.base);
|
||||
app.use(router);
|
||||
app.use(VueGridLayout);
|
||||
app.use(createPinia());
|
||||
app.mount("#y-code-app");
|
||||
}
|
||||
|
||||
function setStyleSheet(styles: Object = {}) {
|
||||
const styleEle = document.createElement("style");
|
||||
styleEle.type = "text/css";
|
||||
styleEle.innerHTML = `
|
||||
:root {
|
||||
--primary-color: ${styles.primaryColor || "#1677ff"};
|
||||
--primary-light-color: ${styles.primaryLightColor || "#4096ff"};
|
||||
--table-head-bg-color: ${styles.tableHeadBgColor || "#fafafa"};
|
||||
--table-head-font-color: ${styles.tableHeadFontColor || "#191919"};
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(styleEle);
|
||||
}
|
||||
|
||||
const __POWERED_BY_QIANKUN__ =
|
||||
qiankunWindow?.__POWERED_BY_QIANKUN__ ||
|
||||
window?.proxy?.__POWERED_BY_QIANKUN__;
|
||||
|
||||
if (__POWERED_BY_QIANKUN__) {
|
||||
renderWithQiankun({
|
||||
bootstrap() {
|
||||
console.log("bootstrap");
|
||||
return Promise.resolve();
|
||||
},
|
||||
mount(props) {
|
||||
console.log("mount");
|
||||
render(props);
|
||||
return Promise.resolve();
|
||||
},
|
||||
unmount() {
|
||||
console.log("unmount");
|
||||
if (app) {
|
||||
app.unmount();
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
update() {},
|
||||
});
|
||||
} else {
|
||||
render();
|
||||
}
|
||||
26
apps/y-code-v1/src/plugins/antv-g2plot/column.vue
Normal file
26
apps/y-code-v1/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);
|
||||
</script>
|
||||
27
apps/y-code-v1/src/plugins/antv-g2plot/line.vue
Normal file
27
apps/y-code-v1/src/plugins/antv-g2plot/line.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<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);
|
||||
</script>
|
||||
26
apps/y-code-v1/src/plugins/antv-g2plot/pie.vue
Normal file
26
apps/y-code-v1/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);
|
||||
</script>
|
||||
91
apps/y-code-v1/src/plugins/antv-g2plot/useChart.js
Normal file
91
apps/y-code-v1/src/plugins/antv-g2plot/useChart.js
Normal file
@@ -0,0 +1,91 @@
|
||||
import { onBeforeUnmount, onMounted, ref, watch } from "vue";
|
||||
import { cloneDeep } from "lodash-es";
|
||||
|
||||
export default function useChart(ChartClass, props) {
|
||||
const chart = ref(null); // 表格实例
|
||||
const chartOptions = ref(null); // 图表配置
|
||||
const container = ref(null); // 渲染图表元素
|
||||
const { onReady, onEvent } = props.config;
|
||||
|
||||
// 全局事件侦听器
|
||||
let handler;
|
||||
|
||||
onMounted(() => {
|
||||
chartOptions.value = cloneDeep(props.config);
|
||||
|
||||
// 实例化图表
|
||||
const chartInstance = new ChartClass(container.value, { ...props.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(
|
||||
() => props.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,
|
||||
};
|
||||
}
|
||||
8
apps/y-code-v1/src/router/guards.ts
Normal file
8
apps/y-code-v1/src/router/guards.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import type { NavigationGuardWithThis } from 'vue-router';
|
||||
|
||||
const titleGuard: NavigationGuardWithThis<undefined> = (to, from, next) => {
|
||||
next();
|
||||
document.title = to.meta.title ? `${to.meta.title} | 悦码` : '悦码';
|
||||
};
|
||||
|
||||
export { titleGuard };
|
||||
23
apps/y-code-v1/src/router/index.ts
Normal file
23
apps/y-code-v1/src/router/index.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { createRouter, createWebHistory, type Router } from "vue-router";
|
||||
import { titleGuard } from "./guards";
|
||||
import routeList from "./routes";
|
||||
import { qiankunWindow } from "vite-plugin-qiankun/dist/helper";
|
||||
|
||||
let router: Router | null = null;
|
||||
export const createProjectRouter = (base = "") => {
|
||||
const __POWERED_BY_QIANKUN__ =
|
||||
qiankunWindow?.__POWERED_BY_QIANKUN__ ||
|
||||
window.proxy?.__POWERED_BY_QIANKUN__;
|
||||
router = createRouter({
|
||||
history: createWebHistory(
|
||||
base || (__POWERED_BY_QIANKUN__ ? "/y-code-app/" : "")
|
||||
),
|
||||
routes: routeList,
|
||||
});
|
||||
|
||||
// 全局前置守卫
|
||||
router.beforeEach(titleGuard);
|
||||
return router;
|
||||
};
|
||||
|
||||
export default router;
|
||||
117
apps/y-code-v1/src/router/routes.ts
Normal file
117
apps/y-code-v1/src/router/routes.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
import Layout from "@/layout/index.vue";
|
||||
import {
|
||||
HomeOutlined,
|
||||
BarChartOutlined,
|
||||
AppstoreOutlined,
|
||||
} from "@ant-design/icons-vue";
|
||||
import { h } from "vue";
|
||||
import type { VNode, RendererNode, RendererElement } from "vue";
|
||||
|
||||
export interface RouteType {
|
||||
path: string;
|
||||
name: string;
|
||||
component?: any;
|
||||
meta: { title?: string };
|
||||
isMenu?: boolean;
|
||||
redirect?: string;
|
||||
children: RouteType[];
|
||||
icon?: () => VNode<
|
||||
RendererNode,
|
||||
RendererElement,
|
||||
{
|
||||
[key: string]: any;
|
||||
}
|
||||
>;
|
||||
}
|
||||
|
||||
const routeList: RouteType[] = [
|
||||
{
|
||||
path: "/",
|
||||
name: "layout",
|
||||
component: Layout,
|
||||
meta: { title: "首页" },
|
||||
children: [
|
||||
{
|
||||
path: "",
|
||||
name: "-",
|
||||
meta: {},
|
||||
children: [],
|
||||
redirect: "/config-manage/project-cfg",
|
||||
},
|
||||
{
|
||||
path: "/config-manage",
|
||||
name: "config-manage",
|
||||
isMenu: true,
|
||||
meta: { title: "配置管理" },
|
||||
icon: () => h(HomeOutlined),
|
||||
children: [
|
||||
{
|
||||
path: "project-cfg",
|
||||
name: "project-cfg",
|
||||
component: () =>
|
||||
import("@/views/config-manage/project-cfg/index.vue"),
|
||||
meta: { title: "项目配置" },
|
||||
isMenu: true,
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
path: "module-cfg",
|
||||
name: "module-cfg",
|
||||
component: () =>
|
||||
import("@/views/config-manage/module-cfg/index.vue"),
|
||||
meta: { title: "数据来源配置" },
|
||||
isMenu: true,
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "/view-all-manage",
|
||||
name: "view-all-manage",
|
||||
isMenu: true,
|
||||
meta: { title: "视图管理" },
|
||||
icon: () => h(BarChartOutlined),
|
||||
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: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "/page-show-info",
|
||||
name: "page-show-info",
|
||||
isMenu: true,
|
||||
meta: { title: "视图预览" },
|
||||
icon: () => h(AppstoreOutlined),
|
||||
children: [
|
||||
{
|
||||
path: "page-info",
|
||||
name: "page-info",
|
||||
component: () =>
|
||||
import("@/views/page-show-info/page-info/index.vue"),
|
||||
meta: { title: "项目报表" },
|
||||
isMenu: true,
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default routeList;
|
||||
16
apps/y-code-v1/src/stores/useUserInfoStore.ts
Normal file
16
apps/y-code-v1/src/stores/useUserInfoStore.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { readonly, ref } from 'vue';
|
||||
import { defineStore } from 'pinia';
|
||||
import { getUserInfo } from '@/api/common';
|
||||
import type { UserInfoType } from '@/api/common';
|
||||
|
||||
export const useUserInfoStore = defineStore('userInfoStore', () => {
|
||||
const userInfo = ref<UserInfoType>();
|
||||
|
||||
const fetchUserInfo = () => {
|
||||
getUserInfo().then((res) => {
|
||||
userInfo.value = res.data;
|
||||
});
|
||||
};
|
||||
|
||||
return { userInfo: readonly(userInfo), fetchUserInfo };
|
||||
});
|
||||
106
apps/y-code-v1/src/utils/request.ts
Normal file
106
apps/y-code-v1/src/utils/request.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import axios, { AxiosError } from 'axios';
|
||||
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
export interface ResopnseType<T> {
|
||||
reason: string
|
||||
message: string
|
||||
data: T
|
||||
ts: string
|
||||
}
|
||||
// export const YCODE_BASEURL: string = import.meta.env.VITE_YCODE_BASEURL;
|
||||
|
||||
// https://custom-chart-pre-api.shiyue.com
|
||||
|
||||
export const YCODE_BASEURL: string = 'https://custom-chart-pre-api.shiyue.com'
|
||||
|
||||
const requestType = {
|
||||
base: YCODE_BASEURL,
|
||||
};
|
||||
|
||||
const baseAxios: AxiosInstance = axios.create({
|
||||
baseURL: '',
|
||||
timeout: 100000,
|
||||
withCredentials: true,
|
||||
});
|
||||
|
||||
const errorHandle = (error: AxiosError) => {
|
||||
if (error.response) {
|
||||
const status = error.response?.status;
|
||||
switch (status) {
|
||||
case 401:
|
||||
message.warning('请先登录');
|
||||
window.location.href = `${YCODE_BASEURL}/login?redirect=${encodeURIComponent(window.location.href)}`;
|
||||
break;
|
||||
case 403:
|
||||
message.warning('权限不足');
|
||||
break;
|
||||
case 500:
|
||||
message.warning('服务器出错了…… (>_<)');
|
||||
break;
|
||||
default:
|
||||
message.warning('服务器出错了…… (>_<)');
|
||||
break;
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
message.error(error.message);
|
||||
return Promise.reject(error);
|
||||
};
|
||||
//响应拦截器
|
||||
baseAxios.interceptors.response.use((response: AxiosResponse) => {
|
||||
const { data, status } = response;
|
||||
|
||||
if (status !== 200) {
|
||||
return Promise.reject(data);
|
||||
}
|
||||
if (data.code) {
|
||||
if (data.code === 200) {
|
||||
return data;
|
||||
} else {
|
||||
message.warning(data.message);
|
||||
return Promise.reject(data);
|
||||
}
|
||||
}
|
||||
}, errorHandle);
|
||||
|
||||
type RequestConfig = Omit<AxiosRequestConfig, 'baseURL'> & { baseURL?: keyof typeof requestType }
|
||||
const request = <T = any>(config: RequestConfig) => {
|
||||
const host = requestType[config.baseURL || 'base'];
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
baseAxios
|
||||
.request<any, T>({ ...config, baseURL: host })
|
||||
.then((res: T) => {
|
||||
resolve(res);
|
||||
})
|
||||
.catch((err: unknown) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const get = <T = any>(config?: RequestConfig) => {
|
||||
return request<ResopnseType<T>>({
|
||||
...config,
|
||||
method: 'GET',
|
||||
});
|
||||
};
|
||||
|
||||
const post = <T = any>(config?: RequestConfig) =>
|
||||
request<ResopnseType<T>>({
|
||||
...config,
|
||||
method: 'POST',
|
||||
});
|
||||
|
||||
const put = <T = any>(config?: RequestConfig) =>
|
||||
request<ResopnseType<T>>({
|
||||
...config,
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
const del = <T = any>(config?: { url: string }) => request<ResopnseType<T>>({ ...config, method: 'DELETE' });
|
||||
|
||||
export { get, post, del, put, request };
|
||||
@@ -0,0 +1,227 @@
|
||||
<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"
|
||||
@change="toGetDbTable"
|
||||
/>
|
||||
</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="original_type">
|
||||
<a-radio-group v-model:value="formData.original_type">
|
||||
<a-radio :value="1">自定义</a-radio>
|
||||
<a-radio :value="2">指定表</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
v-if="formData.original_type === 1"
|
||||
label="sql数据源"
|
||||
name="original_sql"
|
||||
>
|
||||
<a-input
|
||||
placeholder="请输入sql数据源"
|
||||
v-model:value="formData.original_sql"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
v-if="formData.original_type === 2"
|
||||
label="数据表"
|
||||
name="table"
|
||||
>
|
||||
<a-select
|
||||
placeholder="请先选择项目再选择数据表"
|
||||
v-model:value="formData.table"
|
||||
:options="tableTypes"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="数据库特殊配置" name="is_other_database">
|
||||
<a-switch
|
||||
v-model:checked="formData.is_other_database"
|
||||
:checkedValue="1"
|
||||
:unCheckedValue="0"
|
||||
@change="isOtherChange"
|
||||
/>
|
||||
</a-form-item>
|
||||
<template v-if="formData.is_other_database">
|
||||
<a-form-item label="数据库驱动" name="drive_type">
|
||||
<a-radio-group v-model:value="formData.drive_type">
|
||||
<a-radio :value="1">MySQL</a-radio>
|
||||
<a-radio :value="2">Click House</a-radio>
|
||||
</a-radio-group>
|
||||
</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-password
|
||||
placeholder="请输入数据库密码"
|
||||
v-model:value="formData.database_password"
|
||||
/>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</template>
|
||||
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from "vue";
|
||||
import { getDbTableSelect } 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" },
|
||||
],
|
||||
original_type: [{ required: true, message: "请选择", trigger: "submit" }],
|
||||
original_sql: [{ required: true, message: "请输入", trigger: "submit" }],
|
||||
table: [{ required: true, message: "请选择", trigger: "submit" }],
|
||||
});
|
||||
|
||||
const tableTypes = ref([]);
|
||||
const formRef = ref();
|
||||
const formData = ref({
|
||||
project_id: undefined,
|
||||
modular_name: undefined,
|
||||
is_show: 0,
|
||||
original_type: 1, // 1 - 自定义,2 - 指定表
|
||||
original_sql: undefined,
|
||||
table: undefined,
|
||||
is_other_database: 0,
|
||||
drive_type: 1,
|
||||
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 = {
|
||||
modular_id: newVal.modular_id,
|
||||
project_id: newVal.project_id,
|
||||
modular_name: newVal.modular_name,
|
||||
is_show: newVal.is_show,
|
||||
original_type: newVal.original_type,
|
||||
original_sql: newVal.original_sql,
|
||||
table: newVal.table,
|
||||
drive_type: newVal.drive_type,
|
||||
is_other_database: newVal.is_other_database,
|
||||
database_address: newVal.database_address,
|
||||
database_port: newVal.database_port,
|
||||
database_name: newVal.database_name,
|
||||
database_username: newVal.database_username,
|
||||
database_password: newVal.database_password,
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const toGetDbTable = () => {
|
||||
getDbTableSelect({ projectId: formData.value.project_id }).then((res) => {
|
||||
tableTypes.value = res.data;
|
||||
});
|
||||
};
|
||||
|
||||
const isOtherChange = (val) => {
|
||||
formData.value.drive_type = val ? 1 : undefined
|
||||
formData.value.database_address = undefined
|
||||
formData.value.database_port = undefined
|
||||
formData.value.database_name = undefined
|
||||
formData.value.database_username = undefined
|
||||
formData.value.database_password = undefined
|
||||
}
|
||||
|
||||
const resetFormData = () => {
|
||||
formData.value = {
|
||||
project_id: undefined,
|
||||
modular_name: undefined,
|
||||
is_show: 0,
|
||||
original_type: 1, // 1 - 自定义,2 - 指定表
|
||||
original_sql: undefined,
|
||||
table: undefined,
|
||||
is_other_database: 0,
|
||||
drive_type: undefined,
|
||||
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>
|
||||
@@ -0,0 +1,441 @@
|
||||
<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]"
|
||||
allow-clear
|
||||
placeholder="请输入"
|
||||
/>
|
||||
<template v-else>
|
||||
{{ record[column.dataIndex] }}
|
||||
</template>
|
||||
</template>
|
||||
<template v-if="column.dataIndex === 'field_numerical_name'">
|
||||
<a-select
|
||||
v-if="editableData[record.field_id]"
|
||||
v-model:value="record.field_numerical_type_id"
|
||||
:options="fieldNumTypeSel"
|
||||
placeholder="请选择"
|
||||
allow-clear
|
||||
style="width: 120px"
|
||||
>
|
||||
</a-select>
|
||||
<template v-else>
|
||||
{{ record.field_numerical_name }}
|
||||
</template>
|
||||
</template>
|
||||
<template v-if="column.dataIndex === 'show_type'">
|
||||
<a-select
|
||||
v-if="editableData[record.field_id]"
|
||||
v-model:value="record.show_type"
|
||||
:options="showTypeOpts"
|
||||
placeholder="请选择"
|
||||
allow-clear
|
||||
style="width: 120px"
|
||||
>
|
||||
</a-select>
|
||||
<template v-else>
|
||||
{{ record.show_type }}
|
||||
</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="请选择"
|
||||
allow-clear
|
||||
style="width: 120px"
|
||||
>
|
||||
</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 === 'is_other_database'">
|
||||
<template v-if="editableData[record.field_id]">
|
||||
<a-switch
|
||||
v-model:checked="record.is_other_database"
|
||||
:checkedValue="1"
|
||||
:unCheckedValue="0"
|
||||
/>
|
||||
<a-button
|
||||
v-if="record.is_other_database"
|
||||
type="link"
|
||||
@click="openSpecialModal(record)"
|
||||
>请填写</a-button
|
||||
>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ record.is_other_database ? "是" : "否" }}
|
||||
</template>
|
||||
</template>
|
||||
<template v-if="column.dataIndex === 'original_type'">
|
||||
<a-select
|
||||
v-if="editableData[record.field_id]"
|
||||
placeholder="请选择"
|
||||
v-model:value="record.original_type"
|
||||
:options="originalTypes"
|
||||
allow-clear
|
||||
>
|
||||
</a-select>
|
||||
<template v-else>
|
||||
{{ record.original_type_name }}
|
||||
</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>
|
||||
<div v-else>
|
||||
<a-button type="link" @click="handleEdit(record)">修改</a-button>
|
||||
<a-popconfirm
|
||||
title="确定删除字段?"
|
||||
@confirm="handleDelete(record)"
|
||||
>
|
||||
<a-button type="link" danger>删除</a-button>
|
||||
</a-popconfirm>
|
||||
</div>
|
||||
</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
|
||||
:open="specialVisible"
|
||||
:width="640"
|
||||
title="数据库特配"
|
||||
:bodyStyle="{ marginTop: '30px' }"
|
||||
@ok="specialVisible = false"
|
||||
@cancel="specialVisible = false"
|
||||
>
|
||||
<a-form :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
||||
<a-form-item label="数据库驱动">
|
||||
<a-radio-group
|
||||
v-model:value="specialModalData[specialModalId].drive_type"
|
||||
>
|
||||
<a-radio :value="1">MySQL</a-radio>
|
||||
<a-radio :value="2">Click House</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item label="数据库地址">
|
||||
<a-input
|
||||
placeholder="请输入数据库地址"
|
||||
v-model:value="specialModalData[specialModalId].database_address"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="数据库端口">
|
||||
<a-input-number
|
||||
placeholder="请输入数据库端口"
|
||||
style="width: 200px"
|
||||
v-model:value="specialModalData[specialModalId].database_port"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="数据库名称">
|
||||
<a-input
|
||||
placeholder="请输入数据库名称"
|
||||
v-model:value="specialModalData[specialModalId].database_name"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="数据库用户">
|
||||
<a-input
|
||||
placeholder="请输入数据库用户"
|
||||
v-model:value="specialModalData[specialModalId].database_username"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="数据库密码">
|
||||
<a-input-password
|
||||
placeholder="请输入数据库密码"
|
||||
v-model:value="specialModalData[specialModalId].database_password"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref, watch } from "vue";
|
||||
import { viewCfgCols, originalTypes, showTypeOpts } from "@/views/config-manage/module-cfg/config";
|
||||
import {
|
||||
getFieldTypeSelect,
|
||||
getFieldNumSelect,
|
||||
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 fieldNumTypeSel = ref([]);
|
||||
const specialVisible = ref(false);
|
||||
const specialModalId = ref();
|
||||
const pageState = reactive({
|
||||
page: 1,
|
||||
perPage: 20,
|
||||
total: 0,
|
||||
});
|
||||
const editableData = reactive({});
|
||||
const specialModalData = reactive({
|
||||
drive_type: undefined,
|
||||
database_address: undefined,
|
||||
database_port: undefined,
|
||||
database_name: undefined,
|
||||
database_username: undefined,
|
||||
database_password: undefined,
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.open,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
toGetList();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
toGetFieldTypes();
|
||||
toGetFieldNumSelect();
|
||||
});
|
||||
|
||||
// 字段搜索类型下拉
|
||||
const toGetFieldTypes = () => {
|
||||
getFieldTypeSelect().then((res) => {
|
||||
fieldTypeSel.value = res.data;
|
||||
});
|
||||
};
|
||||
|
||||
// 字段类型下拉
|
||||
const toGetFieldNumSelect = () => {
|
||||
getFieldNumSelect().then((res) => {
|
||||
fieldNumTypeSel.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,
|
||||
field_numerical_type_id: undefined,
|
||||
is_search: 0,
|
||||
field_type_id: undefined,
|
||||
belong_to_table: undefined,
|
||||
is_other_database: 0,
|
||||
original_type: undefined,
|
||||
original_sql: undefined,
|
||||
sort: 0,
|
||||
drive_type: undefined,
|
||||
database_address: undefined,
|
||||
database_port: undefined,
|
||||
database_name: undefined,
|
||||
database_username: undefined,
|
||||
database_password: undefined,
|
||||
};
|
||||
dataList.value.unshift(item);
|
||||
editableData[item.field_id] = {
|
||||
...item,
|
||||
};
|
||||
};
|
||||
|
||||
const handleEdit = (record) => {
|
||||
editableData[record.field_id] = {
|
||||
...record,
|
||||
};
|
||||
};
|
||||
|
||||
const handleDelete = (record) => {
|
||||
deleteField({
|
||||
field_id: record.field_id,
|
||||
}).then(() => {
|
||||
message.success('删除成功')
|
||||
toGetList();
|
||||
})
|
||||
}
|
||||
|
||||
const handleCancel = (record) => {
|
||||
if (typeof record.field_id === "string") {
|
||||
dataList.value.shift();
|
||||
} else {
|
||||
delete editableData[record.field_id];
|
||||
}
|
||||
};
|
||||
|
||||
const openSpecialModal = (record) => {
|
||||
specialVisible.value = true
|
||||
specialModalId.value = record.field_id
|
||||
if (!specialModalData[record.field_id]) {
|
||||
specialModalData[record.field_id] = {
|
||||
drive_type: undefined,
|
||||
database_address: undefined,
|
||||
database_port: undefined,
|
||||
database_name: undefined,
|
||||
database_username: undefined,
|
||||
database_password: undefined,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleSave = (record) => {
|
||||
const params = {
|
||||
is_search: record.is_search,
|
||||
field_type_id: record.field_type_id,
|
||||
modular_id: props.modularId,
|
||||
original_type: record.original_type,
|
||||
original_sql: record.original_sql,
|
||||
is_other_database: record.is_other_database,
|
||||
};
|
||||
// 如果数据库特配弹框有数据
|
||||
if (specialModalData[record.field_id]) {
|
||||
params.drive_type = specialModalData[record.field_id].drive_type
|
||||
params.database_address = specialModalData[record.field_id].database_address
|
||||
params.database_port = specialModalData[record.field_id].database_port
|
||||
params.database_name = specialModalData[record.field_id].database_name
|
||||
params.database_username = specialModalData[record.field_id].database_username
|
||||
params.database_password = specialModalData[record.field_id].database_password
|
||||
}
|
||||
// 检验必填参数
|
||||
const validateFields = [
|
||||
{ field: 'field_title', msg: "请填写字段标题" },
|
||||
{ field: 'field_name', msg: "请填写字段名称" },
|
||||
{ field: 'field_numerical_type_id', msg: "请选择字段类型" },
|
||||
{ field: 'belong_to_table', msg: "请填写关联表" },
|
||||
{ field: 'show_type', msg: "请选择展示类型" },
|
||||
]
|
||||
for(let i = 0; i < validateFields.length; i++) {
|
||||
const curr = validateFields[i];
|
||||
if (!record[curr.field]) {
|
||||
message.error(curr.msg);
|
||||
return;
|
||||
} else {
|
||||
params[curr.field] = record[curr.field];
|
||||
}
|
||||
}
|
||||
if (record.is_search && !record.field_type_id) {
|
||||
message.error("请选择搜索类型");
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果是编辑操作
|
||||
if (typeof record.field_id === "number") {
|
||||
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;
|
||||
}
|
||||
|
||||
:deep(.ycode-ant-btn) {
|
||||
padding: 4px 8px;
|
||||
}
|
||||
</style>
|
||||
41
apps/y-code-v1/src/views/config-manage/module-cfg/config.ts
Normal file
41
apps/y-code-v1/src/views/config-manage/module-cfg/config.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
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: "original_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_numerical_name",
|
||||
title: "字段类型",
|
||||
align: "center",
|
||||
width: 120,
|
||||
},
|
||||
{ dataIndex: "show_type", title: "展示类型", align: "center", width: 120 },
|
||||
{ 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: "is_other_database", title: "数据库特配", align: "center" },
|
||||
{ dataIndex: "original_type", title: "数据源类型", align: "center" },
|
||||
{ dataIndex: "original_sql", title: "数据源", align: "center", width: 400 },
|
||||
{ dataIndex: "action", title: "操作", align: "center", width: 120 },
|
||||
];
|
||||
|
||||
export const originalTypes = [
|
||||
{ label: "sql", value: 1 },
|
||||
{ label: "json", value: 2 },
|
||||
];
|
||||
|
||||
export const showTypeOpts = [
|
||||
{ label: "文本", value: "text" },
|
||||
{ label: "图片", value: "img" },
|
||||
{ label: "链接", value: "link" },
|
||||
{ label: "富文本", value: "richText" },
|
||||
];
|
||||
233
apps/y-code-v1/src/views/config-manage/module-cfg/index.vue
Normal file
233
apps/y-code-v1/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="90%"
|
||||
: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;
|
||||
}
|
||||
|
||||
.ycode-ant-table-wrapper {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.pagination-box {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
118
apps/y-code-v1/src/views/config-manage/module-cfg/service.js
Normal file
118
apps/y-code-v1/src/views/config-manage/module-cfg/service.js
Normal file
@@ -0,0 +1,118 @@
|
||||
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 getDbTableSelect({ projectId }) {
|
||||
return get({
|
||||
url: `/api/v1/modular/get-database-table-drop`,
|
||||
params: {
|
||||
project_id: projectId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// 字段搜索类型下拉
|
||||
export function getFieldTypeSelect() {
|
||||
return get({
|
||||
url: `/api/v1/field/get-field-type-drop`,
|
||||
});
|
||||
}
|
||||
|
||||
// 字段类型下拉
|
||||
export function getFieldNumSelect() {
|
||||
return get({
|
||||
url: `/api/v1/field/get-field-numerical-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,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
<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" v-model:value="formData.mark" />
|
||||
</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-password
|
||||
placeholder="请输入数据库密码"
|
||||
v-model:value="formData.database_password"
|
||||
/>
|
||||
<a-button type="primary" @click="toCheckDbConnect"
|
||||
>检测数据库连接</a-button
|
||||
>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
<a-form-item label="数据库驱动" name="drive_type">
|
||||
<a-radio-group v-model:value="formData.drive_type">
|
||||
<a-radio :value="1">MySQL</a-radio>
|
||||
<a-radio :value="2">Click House</a-radio>
|
||||
</a-radio-group>
|
||||
</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" },
|
||||
],
|
||||
};
|
||||
|
||||
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,
|
||||
drive_type: 1,
|
||||
});
|
||||
|
||||
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,
|
||||
drive_type: formData.value.drive_type,
|
||||
}).then((res) => {
|
||||
message.success(res.message);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const validateConnect = () => {
|
||||
const fields = {
|
||||
database_name: "请输入数据库名称",
|
||||
database_port: "请输入数据库端口",
|
||||
database_address: "请输入数据库地址",
|
||||
};
|
||||
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,
|
||||
drive_type: 1,
|
||||
};
|
||||
};
|
||||
|
||||
const handleOk = () => {
|
||||
formRef.value.validate().then(() => {
|
||||
emit("ok", formData.value);
|
||||
});
|
||||
};
|
||||
</script>
|
||||
@@ -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'},
|
||||
];
|
||||
201
apps/y-code-v1/src/views/config-manage/project-cfg/index.vue
Normal file
201
apps/y-code-v1/src/views/config-manage/project-cfg/index.vue
Normal file
@@ -0,0 +1,201 @@
|
||||
<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;
|
||||
}
|
||||
|
||||
.ycode-ant-table-wrapper {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.pagination-box {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
@@ -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
apps/y-code-v1/src/views/config-manage/view-cfg/config.ts
Normal file
10
apps/y-code-v1/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
apps/y-code-v1/src/views/config-manage/view-cfg/index.vue
Normal file
181
apps/y-code-v1/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;
|
||||
}
|
||||
|
||||
.ycode-ant-table-wrapper {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.pagination-box {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
40
apps/y-code-v1/src/views/config-manage/view-cfg/service.js
Normal file
40
apps/y-code-v1/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,
|
||||
});
|
||||
}
|
||||
342
apps/y-code-v1/src/views/page-show-info/page-info/index.vue
Normal file
342
apps/y-code-v1/src/views/page-show-info/page-info/index.vue
Normal file
@@ -0,0 +1,342 @@
|
||||
<template>
|
||||
<div class="page-view-wrapp">
|
||||
<div v-if="!isInQiankun" class="project">
|
||||
<span>项目: </span>
|
||||
<a-select
|
||||
style="min-width: 160px"
|
||||
placeholder="请选择项目"
|
||||
v-model:value="projectVal"
|
||||
:options="projectOptions"
|
||||
@change="handleProjectChange"
|
||||
></a-select>
|
||||
</div>
|
||||
<div>
|
||||
<grid-layout
|
||||
v-if="isDraggable"
|
||||
:layout.sync="layoutList"
|
||||
:col-num="2"
|
||||
:is-draggable="true"
|
||||
:is-resizable="false"
|
||||
:is-mirrored="false"
|
||||
:vertical-compact="true"
|
||||
:use-css-transforms="true"
|
||||
>
|
||||
<grid-item
|
||||
v-for="(item, index) in layoutList"
|
||||
:x="item.x"
|
||||
:y="item.y"
|
||||
:w="item.w"
|
||||
:h="item.h"
|
||||
:i="item.i"
|
||||
:key="item.i"
|
||||
drag-allow-from=".vue-draggable-handle"
|
||||
drag-ignore-from=".no-drag"
|
||||
>
|
||||
<div class="view-box view-draggable">
|
||||
<div class="vue-draggable-handle"><BarsOutlined /></div>
|
||||
<div class="content no-drag">
|
||||
<a-spin :spinning="ids[index].loading">
|
||||
<div class="card-content">
|
||||
<y-table
|
||||
v-if="item.data.type === VIEW_TYPE.TABLE"
|
||||
:preview-id="item.id"
|
||||
:filter-config="item.data.filter"
|
||||
:data-list="item.data.data"
|
||||
:column-config="item.data.header"
|
||||
:total="item.data.count"
|
||||
:title="item.data.preview_name"
|
||||
:is-export="item.data.is_export"
|
||||
@toFilt="
|
||||
(params?:object) => {
|
||||
handleSingle(ids[index], params);
|
||||
}
|
||||
"
|
||||
></y-table>
|
||||
<y-chart
|
||||
v-if="item.data.type === VIEW_TYPE.CHART"
|
||||
:chartCfg="item.data.config"
|
||||
:title="item.data.preview_name"
|
||||
:filter-config="item.data.filter"
|
||||
@toFilt="
|
||||
(params?:object) => {
|
||||
handleSingle(ids[index], params);
|
||||
}
|
||||
"
|
||||
></y-chart>
|
||||
</div>
|
||||
</a-spin>
|
||||
</div>
|
||||
</div>
|
||||
</grid-item>
|
||||
</grid-layout>
|
||||
|
||||
<a-row v-else :gutter="[16, 16]">
|
||||
<a-col v-for="(item, index) in layoutList" :span="24">
|
||||
<a-spin :spinning="item.loading">
|
||||
<div>
|
||||
<div class="view-box">
|
||||
<div class="content">
|
||||
<div class="card-content">
|
||||
<y-table
|
||||
v-if="item.data.type === VIEW_TYPE.TABLE"
|
||||
:preview-id="item.id"
|
||||
:filter-config="item.data.filter"
|
||||
:data-list="item.data.data"
|
||||
:column-config="item.data.header"
|
||||
:total="item.data.count"
|
||||
:title="item.data.preview_name"
|
||||
:is-export="item.data.is_export"
|
||||
@toFilt="
|
||||
(params?:object) => {
|
||||
handleSingle(ids[index], params,);
|
||||
}
|
||||
"
|
||||
></y-table>
|
||||
<y-chart
|
||||
v-if="item.data.type === VIEW_TYPE.CHART"
|
||||
:chartCfg="item.data.config"
|
||||
:title="item.data.preview_name"
|
||||
:filter-config="item.data.filter"
|
||||
@toFilt="
|
||||
(params?:object) => {
|
||||
handleSingle(ids[index], params);
|
||||
}
|
||||
"
|
||||
></y-chart>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-spin>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, shallowRef, computed, onMounted, watch } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { BarsOutlined } from "@ant-design/icons-vue";
|
||||
// utils
|
||||
import PLimit from "p-limit";
|
||||
// api
|
||||
import { searchInfo } from "@/api/preview/index";
|
||||
import { getProjectDrop } from "@/api/common";
|
||||
import { getPageInfo } from "./service";
|
||||
import type { SelectProps } from "ant-design-vue";
|
||||
import { qiankunWindow } from 'vite-plugin-qiankun/dist/helper'
|
||||
|
||||
interface ItemDetail {
|
||||
id: number | string;
|
||||
data: any;
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
interface Item {
|
||||
id: number | string;
|
||||
data: any;
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
interface Option extends SelectProps {
|
||||
mark: string;
|
||||
}
|
||||
|
||||
const VIEW_TYPE = {
|
||||
TABLE: "table",
|
||||
CHART: "chart",
|
||||
};
|
||||
|
||||
const SEARCH_TYPE = {
|
||||
SEARCH: "search",
|
||||
INIT: "init",
|
||||
};
|
||||
|
||||
// hooks
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const projectTag = shallowRef();
|
||||
const projectVal = shallowRef();
|
||||
const pageId = shallowRef(route.query.pageId);
|
||||
const projectOptions = shallowRef<Option[]>();
|
||||
const isDraggable = false;
|
||||
|
||||
const isInQiankun = computed(() => {
|
||||
return qiankunWindow?.__POWERED_BY_QIANKUN__ || window?.proxy?.__POWERED_BY_QIANKUN__
|
||||
})
|
||||
|
||||
const layoutList = computed(() => {
|
||||
return ids.value.map((item, index) => {
|
||||
// 当前是第几行
|
||||
const row = Math.floor(index / 2);
|
||||
// 当前是第几列ji
|
||||
const col = index % 2;
|
||||
return {
|
||||
i: item?.id,
|
||||
x: col,
|
||||
y: row,
|
||||
w: 1,
|
||||
h: 3,
|
||||
minH: 3,
|
||||
...item,
|
||||
};
|
||||
});
|
||||
});
|
||||
const ids = ref<Item[]>([]);
|
||||
|
||||
const pLimit = PLimit(2);
|
||||
|
||||
watch(() => route.query.viewId, () => {
|
||||
getPageInfoData()
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
getProjectList();
|
||||
});
|
||||
|
||||
const handleSingle = (info: ItemDetail, otherParams?: object) => {
|
||||
getSinglePreview({ info, otherParams, type: SEARCH_TYPE.SEARCH });
|
||||
};
|
||||
|
||||
const handleProjectChange = (value: string | number, option: Option) => {
|
||||
projectTag.value = option.mark;
|
||||
router.replace({
|
||||
path: route.path,
|
||||
query: {
|
||||
...route.query,
|
||||
projectTag: projectTag.value,
|
||||
},
|
||||
});
|
||||
getPageInfoData();
|
||||
};
|
||||
|
||||
// 请求
|
||||
|
||||
// 获取项目下拉
|
||||
const getProjectList = () => {
|
||||
getProjectDrop()
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
projectOptions.value = res.data;
|
||||
projectTag.value = route.query.projectTag || res.data[0].mark;
|
||||
projectVal.value =
|
||||
projectOptions.value?.find((item) => {
|
||||
return item.mark === route.query.projectTag;
|
||||
})?.value || res.data[0].value;
|
||||
getPageInfoData();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
projectOptions.value = [];
|
||||
})
|
||||
.finally(() => {});
|
||||
};
|
||||
|
||||
// 单个视图请求
|
||||
const getSinglePreview = (data: {
|
||||
info: ItemDetail;
|
||||
otherParams?: object;
|
||||
type?: string;
|
||||
}) => {
|
||||
const { info, otherParams, type } = data;
|
||||
info.loading = true;
|
||||
const params = { previewId: info.id, page: 1, perPage: 20, ...otherParams };
|
||||
searchInfo(params)
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
info.data = res.data;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
info.loading = false;
|
||||
});
|
||||
};
|
||||
|
||||
// 获取页面信息所有的id
|
||||
const getPageInfoData = () => {
|
||||
getPageInfo({ mark:projectTag.value, page_id: pageId.value ?? "-1" })
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
if (route.query.viewId) {
|
||||
ids.value = res.data?.filter((item: any) => {
|
||||
return item.preview_id === Number(route.query.viewId);
|
||||
}).map((item: any) => {
|
||||
return {
|
||||
id: item.preview_id,
|
||||
data: item,
|
||||
loading: false,
|
||||
}
|
||||
})
|
||||
} else {
|
||||
ids.value = res.data?.map((item: any) => {
|
||||
return {
|
||||
id: item.preview_id,
|
||||
data: item,
|
||||
loading: false,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
getAllCardsData();
|
||||
}
|
||||
})
|
||||
.finally(() => {});
|
||||
};
|
||||
|
||||
const fetchFn = (delay: number, info: ItemDetail) => {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve(info);
|
||||
getSinglePreview({ info, type: SEARCH_TYPE.INIT });
|
||||
}, delay);
|
||||
});
|
||||
};
|
||||
|
||||
const getAllCardsData = async () => {
|
||||
let listDB = [];
|
||||
for (let i in ids.value) {
|
||||
listDB.push(pLimit(() => fetchFn(i === "0" ? 200 : 1000, ids.value[i])));
|
||||
}
|
||||
await Promise.all(listDB);
|
||||
//此处的listDB就是最后整合的数据
|
||||
};
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
|
||||
.view-box {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
min-height: 350px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #fff;
|
||||
border-radius: 4px;
|
||||
transition: all 0.3s;
|
||||
&:hover {
|
||||
box-shadow: 0 0 20px 0 #0a103205, 0 14px 40px 0 #0a103208,
|
||||
0 20px 60px 0 #0a10320d;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.project {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.view-draggable {
|
||||
height: auto;
|
||||
min-height: 450px;
|
||||
}
|
||||
|
||||
.vue-draggable-handle {
|
||||
padding: 0 8px 8px 0;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.vue-grid-item {
|
||||
height: auto;
|
||||
}
|
||||
</style>
|
||||
10
apps/y-code-v1/src/views/page-show-info/page-info/service.ts
Normal file
10
apps/y-code-v1/src/views/page-show-info/page-info/service.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { get, post } from "@/utils/request";
|
||||
interface PageInfoParams {
|
||||
mark: string;
|
||||
page_id: number | string;
|
||||
}
|
||||
export const getPageInfo = (data: PageInfoParams) =>
|
||||
get({
|
||||
url: "/api/v1/preview/get-preview-info",
|
||||
params: data,
|
||||
});
|
||||
59
apps/y-code-v1/src/views/page-show-info/page-info/style.less
Normal file
59
apps/y-code-v1/src/views/page-show-info/page-info/style.less
Normal file
@@ -0,0 +1,59 @@
|
||||
@import "@/assets/styles/variable.less";
|
||||
|
||||
// 设置按钮
|
||||
:deep(.ycode-ant-btn-primary) {
|
||||
background-color: @primary-color !important;
|
||||
&:hover {
|
||||
background-color: @primary-light-color !important;
|
||||
}
|
||||
}
|
||||
|
||||
// 设置输入框
|
||||
:deep(
|
||||
.ycode-ant-input-affix-wrapper:not(
|
||||
.ycode-ant-input-affix-wrapper-disabled
|
||||
):hover
|
||||
) {
|
||||
border-color: @primary-light-color !important;
|
||||
}
|
||||
|
||||
// 设置选择框
|
||||
:deep(.ycode-ant-select-selector:hover) {
|
||||
border-color: @primary-light-color !important;
|
||||
}
|
||||
|
||||
:deep(.ycode-ant-select-focused) {
|
||||
.ycode-ant-select-selector {
|
||||
border-color: @primary-light-color !important;
|
||||
}
|
||||
}
|
||||
|
||||
// 设置日期框
|
||||
:deep(.ycode-ant-picker) {
|
||||
&:hover,
|
||||
&-focused {
|
||||
border-color: @primary-light-color !important;
|
||||
}
|
||||
&-active-bar {
|
||||
background-color: @primary-light-color !important;
|
||||
}
|
||||
}
|
||||
|
||||
// 设置表格
|
||||
:deep(.ycode-ant-table-thead > tr > th) {
|
||||
background-color: @table-head-bg-color !important;
|
||||
color: @table-head-font-color !important;
|
||||
}
|
||||
|
||||
// 设置分页器
|
||||
:deep(.ycode-ant-pagination-item) {
|
||||
&-active {
|
||||
border-color: @primary-color !important;
|
||||
a {
|
||||
color: @primary-color !important;
|
||||
}
|
||||
}
|
||||
&-link-icon {
|
||||
color: @primary-color !important;
|
||||
}
|
||||
}
|
||||
538
apps/y-code-v1/src/views/view-all-manage/create-view/index.vue
Normal file
538
apps/y-code-v1/src/views/view-all-manage/create-view/index.vue
Normal file
@@ -0,0 +1,538 @@
|
||||
<template>
|
||||
<div class="normal-container">
|
||||
<div class="view-create-box">
|
||||
<div class="left-box">
|
||||
<a-form :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
|
||||
<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-select
|
||||
placeholder="请选择展示类型"
|
||||
:options="showTypeSel"
|
||||
v-model:value="showTypeId"
|
||||
@change="onShowTypeChange"
|
||||
></a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="是否导出" v-if="showTypeId === 1">
|
||||
<a-switch
|
||||
v-model:checked="isExport"
|
||||
:checkedValue="1"
|
||||
:unCheckedValue="0"
|
||||
>
|
||||
</a-switch>
|
||||
</a-form-item>
|
||||
<a-form-item label="字段" v-if="fieldList.length">
|
||||
<a-checkbox-group 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-form-item>
|
||||
<a-form-item label="x轴" v-if="xDataList.length">
|
||||
<a-radio-group
|
||||
:options="xDataList"
|
||||
v-model:value="xDataId"
|
||||
></a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item label="y轴" v-if="yDataList.length">
|
||||
<a-checkbox-group
|
||||
:options="yDataList"
|
||||
v-model:value="yDataId"
|
||||
></a-checkbox-group>
|
||||
</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-name">
|
||||
<div class="title">{{ previewData.preview_name }}</div>
|
||||
</div>
|
||||
<div class="y-table-filter">
|
||||
<div
|
||||
v-for="item 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"
|
||||
/>
|
||||
<a-range-picker
|
||||
v-if="item.type === 'time'"
|
||||
class="date-item"
|
||||
v-model:value="previewData.filterData[item.name]"
|
||||
@change="toFilt"
|
||||
/>
|
||||
<a-range-picker
|
||||
v-else-if="item.type === 'date_time'"
|
||||
class="date-time-item"
|
||||
v-model:value="previewData.filterData[item.name]"
|
||||
show-time
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
@change="toFilt"
|
||||
/>
|
||||
<!-- 数值区间 -->
|
||||
<div
|
||||
v-else-if="item.type === 'number_range' && previewData.filterData[item.name]"
|
||||
class="number_range_box"
|
||||
>
|
||||
<a-input-number
|
||||
placeholder="最小值"
|
||||
style="width: 140px"
|
||||
v-model:value="previewData.filterData[item.name].min"
|
||||
@change="toFilt"
|
||||
/>
|
||||
<span class="divider"> - </span>
|
||||
<a-input-number
|
||||
placeholder="最大值"
|
||||
style="width: 140px"
|
||||
v-model:value="previewData.filterData[item.name].max"
|
||||
@change="toFilt"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="previewData.isExport" class="filter-item">
|
||||
<a-button type="primary">导出</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="y-table-content">
|
||||
<a-table
|
||||
:columns="previewData.columnConfig"
|
||||
:data-source="previewData.dataList"
|
||||
:pagination="false"
|
||||
:scroll="{ x: 1000, y: `calc(100vh - 260px)` }"
|
||||
size="small"
|
||||
bordered
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<a-image
|
||||
v-if="column.show_type === 'img'"
|
||||
:src="record[column.dataIndex]"
|
||||
:width="160"
|
||||
/>
|
||||
<a
|
||||
v-else-if="column.show_type === 'link'"
|
||||
target="_blank"
|
||||
:href="record[column.dataIndex]"
|
||||
>{{ record[column.dataIndex] }}</a
|
||||
>
|
||||
<div
|
||||
v-else-if="column.show_type === 'richText'"
|
||||
v-html="record[column.dataIndex]"
|
||||
></div>
|
||||
<template v-else>{{ record[column.dataIndex] }}</template>
|
||||
</template>
|
||||
</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>
|
||||
<y-chart
|
||||
v-else-if="previewData.type === 'chart'"
|
||||
:chart-cfg="previewData.chartCfg"
|
||||
:filter-config="previewData.filter"
|
||||
@toFilt="() => {toPreview({})}"
|
||||
></y-chart>
|
||||
<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,
|
||||
getShowTypeSelect,
|
||||
getFieldOpts,
|
||||
} from "./service";
|
||||
import { message } from "ant-design-vue";
|
||||
import { BarChartOutlined } from "@ant-design/icons-vue";
|
||||
import yChart from "@/components/common/y-chart.vue";
|
||||
import { cloneDeep } from "lodash-es";
|
||||
|
||||
const projectSel = ref([]); // 项目下拉
|
||||
const modularSel = ref([]) // 数据来源下拉
|
||||
const showTypeSel = ref([]); // 展示类型下拉
|
||||
const fieldList = ref([]); // 字段列表
|
||||
const xDataList = ref([]); // x轴数据列表
|
||||
const yDataList = ref([]); // y轴数据列表
|
||||
const projectId = ref();
|
||||
const modularId = ref();
|
||||
const showTypeId = ref();
|
||||
const fieldIds = ref([]);
|
||||
const xDataId = ref();
|
||||
const yDataId = ref();
|
||||
const isExport = ref(0);
|
||||
|
||||
const previewLoading = ref(false);
|
||||
const nameVisible = ref(false);
|
||||
const previewName = ref();
|
||||
|
||||
const previewData = reactive({
|
||||
type: "",
|
||||
filterConfig: [], // 筛选条件
|
||||
columnConfig: [], // 表格表头
|
||||
dataList: [], // 表格数据
|
||||
filterData: {},
|
||||
config: {},
|
||||
filter: [],
|
||||
page: 1,
|
||||
perPage: 20,
|
||||
total: 0,
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
toGetProModularField();
|
||||
toGetShowTypes();
|
||||
});
|
||||
|
||||
const toGetProModularField = () => {
|
||||
getProModularField().then((res) => {
|
||||
projectSel.value = res.data;
|
||||
});
|
||||
};
|
||||
|
||||
// 获取展示类型下拉
|
||||
const toGetShowTypes = () => {
|
||||
getShowTypeSelect().then((res) => {
|
||||
showTypeSel.value = res.data;
|
||||
});
|
||||
};
|
||||
|
||||
// 获取字段列表
|
||||
const toGetFieldOpts = () => {
|
||||
getFieldOpts({
|
||||
modularId: modularId.value,
|
||||
showTypeId: showTypeId.value,
|
||||
}).then((res) => {
|
||||
fieldList.value = res.data.list ? tranformList(res.data.list) : [];
|
||||
xDataList.value = res.data.x_data ? tranformList(res.data.x_data) : [];
|
||||
yDataList.value = res.data.y_data ? tranformList(res.data.y_data) : [];
|
||||
if (!fieldList.value.length) {
|
||||
fieldIds.value = [];
|
||||
} else {
|
||||
xDataId.value = undefined;
|
||||
yDataId.value = [];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const onProjectChange = (val) => {
|
||||
// 重置数据
|
||||
const target = projectSel.value.find((item) => item.value === val);
|
||||
modularSel.value = target.child;
|
||||
modularId.value = undefined;
|
||||
resetSelectData();
|
||||
resetPreviewData();
|
||||
};
|
||||
|
||||
const onModularChange = () => {
|
||||
resetSelectData();
|
||||
resetPreviewData();
|
||||
};
|
||||
|
||||
const onShowTypeChange = () => {
|
||||
toGetFieldOpts();
|
||||
};
|
||||
|
||||
const tranformList = (list) => {
|
||||
return list.map((item) => {
|
||||
return {
|
||||
label: item.field_name,
|
||||
value: item.field_id,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// 重置配置数据
|
||||
const resetSelectData = () => {
|
||||
showTypeId.value = undefined
|
||||
isExport.value = 0
|
||||
fieldList.value = [];
|
||||
fieldIds.value = [];
|
||||
xDataList.value = [];
|
||||
yDataList.value = [];
|
||||
xDataId.value = undefined;
|
||||
yDataId.value = [];
|
||||
}
|
||||
|
||||
// 重置预览数据
|
||||
const resetPreviewData = () => {
|
||||
previewData.type = "";
|
||||
previewData.filterConfig = [];
|
||||
previewData.columnConfig = [];
|
||||
previewData.dataList = [];
|
||||
previewData.filterData = {};
|
||||
previewData.config = {};
|
||||
previewData.page = 1;
|
||||
previewData.perPage = 20;
|
||||
previewData.total = 0;
|
||||
};
|
||||
|
||||
// 请求预览数据
|
||||
const toPreview = ({filter}) => {
|
||||
previewLoading.value = true;
|
||||
let filterData
|
||||
if (!filter) {
|
||||
const cloneFilter = cloneDeep(previewData.filterConfig)
|
||||
filterData = cloneFilter
|
||||
.filter((item) => {
|
||||
return previewData.filterData[item.name] !== undefined && previewData.filterData[item.name] !== null;
|
||||
})
|
||||
.map((item) => {
|
||||
if (item.type === 'time' && previewData.filterData[item.name]) {
|
||||
// 日期类型的参数
|
||||
return {
|
||||
name: item.name,
|
||||
type: item.type,
|
||||
start_time: previewData.filterData[item.name][0].format('YYYY-MM-DD'),
|
||||
end_time: previewData.filterData[item.name][1].format('YYYY-MM-DD'),
|
||||
};
|
||||
} else if (item.type === 'date_time' && previewData.filterData[item.name]) {
|
||||
// 带时分的日期类型参数
|
||||
return {
|
||||
name: item.name,
|
||||
type: item.type,
|
||||
start_time: previewData.filterData[item.name][0].format('YYYY-MM-DD HH:mm') + ':00',
|
||||
end_time: previewData.filterData[item.name][1].format('YYYY-MM-DD HH:mm') + ':59',
|
||||
};
|
||||
} else if (item.type === 'number_range') {
|
||||
// 数值区间
|
||||
return {
|
||||
name: item.name,
|
||||
type: item.type,
|
||||
min: previewData.filterData[item.name].min ? String(previewData.filterData[item.name].min) : '',
|
||||
max: previewData.filterData[item.name].max ? String(previewData.filterData[item.name].max) : '',
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
name: item.name,
|
||||
type: item.type,
|
||||
value: previewData.filterData[item.name],
|
||||
};
|
||||
}
|
||||
});
|
||||
} else {
|
||||
filterData = filter;
|
||||
}
|
||||
|
||||
preview({
|
||||
modularId: modularId.value,
|
||||
fieldIds: fieldIds.value.toString(),
|
||||
page: previewData.page,
|
||||
perPage: previewData.perPage,
|
||||
filter: filterData,
|
||||
showTypeId: showTypeId.value,
|
||||
xDataId: xDataId.value?.toString(),
|
||||
yDataId: yDataId.value?.toString(),
|
||||
isExport: isExport.value,
|
||||
})
|
||||
.then((res) => {
|
||||
previewData.type = res.data.type;
|
||||
if (res.data.type === "table") {
|
||||
previewData.filterConfig = res.data.filter;
|
||||
previewData.filterConfig.forEach((item) => {
|
||||
if (item.type === 'number_range' && !previewData.filterData[item.name]) {
|
||||
previewData.filterData[item.name] = {
|
||||
...item,
|
||||
min: undefined,
|
||||
max: undefined,
|
||||
};
|
||||
} else {
|
||||
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;
|
||||
previewData.isExport = res.data.is_export
|
||||
} else {
|
||||
previewData.chartCfg = res.data.config;
|
||||
previewData.filter = res.data.filter;
|
||||
}
|
||||
})
|
||||
.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,
|
||||
showTypeId: showTypeId.value,
|
||||
xDataId: xDataId.value?.toString(),
|
||||
yDataId: yDataId.value?.toString(),
|
||||
isExport: isExport.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-name {
|
||||
.title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
.y-table-filter {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.filter-item {
|
||||
margin-right: 10px;
|
||||
margin-bottom: 6px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.input-item {
|
||||
width: 180px;
|
||||
}
|
||||
.date-item {
|
||||
width: 240px;
|
||||
}
|
||||
.date-time-item {
|
||||
width: 300px;
|
||||
}
|
||||
.number_range_box {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
.divider {
|
||||
margin: 0 4px;
|
||||
}
|
||||
}
|
||||
.y-table-content {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.pagination-box {
|
||||
text-align: center;
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,59 @@
|
||||
import { get, post } from "@/utils/request";
|
||||
|
||||
// 项目-表-字段下拉
|
||||
export function getProModularField() {
|
||||
return get({
|
||||
url: "/api/v1/field/get-project-modular-field-drop",
|
||||
});
|
||||
}
|
||||
|
||||
// 展示类型下拉
|
||||
export function getShowTypeSelect() {
|
||||
return get({
|
||||
url: `/api/v1/modular/get-show-type-drop`,
|
||||
});
|
||||
}
|
||||
|
||||
// 字段列表
|
||||
export function getFieldOpts({ modularId, showTypeId }) {
|
||||
return get({
|
||||
url: "/api/v1/preview/get-preview-field",
|
||||
params: {
|
||||
modular_id: modularId,
|
||||
show_type_id: showTypeId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// 预览
|
||||
export function preview({ modularId, fieldIds, page, perPage, filter, showTypeId, xDataId, yDataId, isExport }) {
|
||||
return post({
|
||||
url: "api/v1/preview/view",
|
||||
data: {
|
||||
modular_id: modularId,
|
||||
field_ids: fieldIds,
|
||||
page,
|
||||
per_page: perPage,
|
||||
filter,
|
||||
show_type_id: showTypeId,
|
||||
x_data_id: xDataId,
|
||||
y_data_id: yDataId,
|
||||
is_export: isExport,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// 点击保存
|
||||
export function saveView({ modularId, fieldIds, previewName, showTypeId, xDataId, yDataId }) {
|
||||
return post({
|
||||
url: "api/v1/preview/save",
|
||||
data: {
|
||||
modular_id: modularId,
|
||||
field_ids: fieldIds,
|
||||
preview_name: previewName,
|
||||
show_type_id: showTypeId,
|
||||
x_data_id: xDataId,
|
||||
y_data_id: yDataId,
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export const viewListCols = [
|
||||
{ dataIndex: 'preview_id', title: 'id', align: 'center' },
|
||||
{ dataIndex: 'preview_name', title: '视图名称', align: 'center' },
|
||||
{ dataIndex: 'created_at', title: '创建时间', align: 'center' },
|
||||
{ dataIndex: 'action', title: '操作', align: 'center' },
|
||||
];
|
||||
239
apps/y-code-v1/src/views/view-all-manage/view-list/index.vue
Normal file
239
apps/y-code-v1/src/views/view-all-manage/view-list/index.vue
Normal file
@@ -0,0 +1,239 @@
|
||||
<template>
|
||||
<div class="normal-container">
|
||||
<div class="view-list-box">
|
||||
<div class="left-box">
|
||||
<a-form :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
|
||||
<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'"
|
||||
:previewId="selectedRowId"
|
||||
:filter-config="selectViewInfo.filter"
|
||||
:data-list="selectViewInfo.data"
|
||||
:column-config="selectViewInfo.header"
|
||||
:total="selectViewInfo.count"
|
||||
:title="selectViewInfo.preview_name"
|
||||
:is-export="selectViewInfo.is_export"
|
||||
@toFilt="
|
||||
(params) => {
|
||||
toGetViewInfo(params);
|
||||
}"
|
||||
/>
|
||||
<y-chart
|
||||
v-else-if="selectViewInfo.type === 'chart'"
|
||||
:chartCfg="selectViewInfo.config"
|
||||
:title="selectViewInfo.preview_name"
|
||||
:filter-config="selectViewInfo.filter"
|
||||
@toFilt="toGetViewInfo"
|
||||
/>
|
||||
<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: [],
|
||||
config: {
|
||||
line: {
|
||||
data: []
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const pageState = reactive({
|
||||
page: 1,
|
||||
perPage: 20,
|
||||
total: 0,
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
toGetProModular();
|
||||
});
|
||||
|
||||
const toGetProModular = () => {
|
||||
getProModular().then((res) => {
|
||||
projectSel.value = res.data;
|
||||
if (res.data.length) {
|
||||
projectId.value = res.data[0].value;
|
||||
onProjectChange(projectId.value)
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
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;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
:deep(.ycode-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>
|
||||
@@ -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,
|
||||
},
|
||||
});
|
||||
}
|
||||
15
apps/y-code-v1/tsconfig.app.json
Normal file
15
apps/y-code-v1/tsconfig.app.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||
"include": ["env.d.ts", "src/**/*", "src/**/*.vue", "src/**/*.tsx", "components.d.ts", "*.d.ts"],
|
||||
"exclude": ["src/**/__tests__/*"],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
},
|
||||
"target": "esnext"
|
||||
}
|
||||
}
|
||||
11
apps/y-code-v1/tsconfig.json
Normal file
11
apps/y-code-v1/tsconfig.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.node.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
19
apps/y-code-v1/tsconfig.node.json
Normal file
19
apps/y-code-v1/tsconfig.node.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"extends": "@tsconfig/node20/tsconfig.json",
|
||||
"include": [
|
||||
"vite.config.*",
|
||||
"vitest.config.*",
|
||||
"cypress.config.*",
|
||||
"nightwatch.conf.*",
|
||||
"playwright.config.*"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"noEmit": true,
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"types": ["node"]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user