feat(数据源): 样式优化
@@ -28,6 +28,7 @@ module.exports = typescriptEslint.config(
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||
'vue/multi-word-component-names': 'off',
|
||||
'vue/no-required-prop-with-default': 'off',
|
||||
'vue/no-v-html': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/ban-ts-comment': 'warn',
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 715 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 400 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8 8.00004C8.73638 8.00004 9.33334 8.59699 9.33334 9.33337C9.33334 9.82689 9.0652 10.2578 8.66667 10.4883V11.6667C8.66667 11.8508 8.51743 12 8.33334 12H7.66667C7.48257 12 7.33334 11.8508 7.33334 11.6667V10.4883C6.9348 10.2578 6.66667 9.82689 6.66667 9.33337C6.66667 8.59699 7.26362 8.00004 8 8.00004Z" fill="#646A73"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 1.33337C10.025 1.33337 11.6667 2.97337 11.6667 5.00004V5.33337H14C14.3683 5.33337 14.6667 5.63337 14.6667 6.00004V14C14.6667 14.3667 14.3683 14.6667 14 14.6667H2C1.63167 14.6667 1.33334 14.3667 1.33334 14V6.00004C1.33334 5.63337 1.63167 5.33337 2 5.33337H4.33334V5.00004C4.33334 2.97337 5.975 1.33337 8 1.33337ZM2.66667 13.3334H13.3333V6.66671H2.66667V13.3334ZM8 2.66671C6.71134 2.66671 5.66667 3.71004 5.66667 5.00004V5.33337H10.3333V5.00004C10.3333 3.71004 9.28867 2.66671 8 2.66671Z" fill="#646A73"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 987 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 14C11.3137 14 14 11.3137 14 7.99996C14 4.68625 11.3137 1.99996 8 1.99996C4.68629 1.99996 2 4.68625 2 7.99996C2 11.3137 4.68629 14 8 14ZM8 15.3333C3.94991 15.3333 0.666664 12.05 0.666664 7.99996C0.666664 3.94987 3.94991 0.666626 8 0.666626C12.0501 0.666626 15.3333 3.94987 15.3333 7.99996C15.3333 12.05 12.0501 15.3333 8 15.3333ZM7.66666 10.6666H8.33333C8.51742 10.6666 8.66666 10.8159 8.66666 11V11.6666C8.66666 11.8507 8.51742 12 8.33333 12H7.66666C7.48257 12 7.33333 11.8507 7.33333 11.6666V11C7.33333 10.8159 7.48257 10.6666 7.66666 10.6666ZM5.36657 6.18777C5.37504 6.11325 5.38372 6.05127 5.3926 6.0018C5.52256 5.27808 5.85169 4.74585 6.38001 4.4051C6.79377 4.135 7.30221 3.99996 7.90532 3.99996C8.69779 3.99996 9.35612 4.19187 9.88034 4.57569C10.4046 4.95951 10.6667 5.52812 10.6667 6.28155C10.6667 6.74356 10.5527 7.1327 10.3248 7.449C10.1915 7.64091 9.93557 7.88613 9.55687 8.18466L9.18343 8.47785C8.98005 8.63778 8.84505 8.82435 8.77843 9.03759C8.7603 9.09578 8.74436 9.20396 8.73063 9.36212C8.71568 9.5344 8.57147 9.66663 8.39855 9.66663H7.65227C7.46818 9.66662 7.31894 9.51739 7.31894 9.33329C7.31894 9.32304 7.31941 9.31279 7.32036 9.30258C7.36328 8.83865 7.40925 8.54508 7.45825 8.42188C7.54941 8.19265 7.78435 7.92878 8.16305 7.63025L8.547 7.32639C8.67324 7.23044 9.32371 6.77239 9.32371 6.36684C9.32371 5.9613 9.25306 5.81213 9.04944 5.58956C8.84582 5.36699 8.38746 5.29535 7.9737 5.29535C7.56694 5.29535 7.21936 5.40337 7.0493 5.67702C6.97053 5.80376 6.90612 5.93897 6.85967 6.07692C6.84139 6.13119 6.8252 6.19947 6.81109 6.28176C6.78365 6.44178 6.64491 6.55875 6.48255 6.55875H5.69777C5.51368 6.55876 5.36443 6.40952 5.36443 6.22542C5.36443 6.21284 5.36515 6.20027 5.36657 6.18777Z" fill="#646A73"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="13" height="12" viewBox="0 0 13 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.66663 0.75C9.56612 0.75 11.9166 3.10051 11.9166 6C11.9166 8.8995 9.56612 11.25 6.66663 11.25H1.91663C1.64048 11.25 1.41663 11.0261 1.41663 10.75V6C1.41663 3.10051 3.76713 0.75 6.66663 0.75ZM2.41663 10.25H6.66663C9.01384 10.25 10.9166 8.3472 10.9166 5.99999C10.9166 3.65278 9.01384 1.74999 6.66663 1.74999C4.31942 1.74999 2.41663 3.65278 2.41663 5.99999V10.25ZM4.41663 6.75H6.91663C7.0547 6.75 7.16663 6.86193 7.16663 7V7.5C7.16663 7.63807 7.0547 7.75 6.91663 7.75H4.41663C4.27855 7.75 4.16663 7.63807 4.16663 7.5V7C4.16663 6.86193 4.27855 6.75 4.41663 6.75ZM4.41663 4.74999H8.91663C9.0547 4.74999 9.16663 4.86192 9.16663 4.99999V5.49999C9.16663 5.63806 9.0547 5.74999 8.91663 5.74999H4.41663C4.27855 5.74999 4.16663 5.63806 4.16663 5.49999V4.99999C4.16663 4.86192 4.27855 4.74999 4.41663 4.74999Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 928 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="17" height="16" viewBox="0 0 17 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.35493 1.6883C2.58222 1.46101 2.8905 1.33331 3.21194 1.33331H14.1213C14.4427 1.33331 14.751 1.46101 14.9783 1.6883C15.2056 1.91559 15.3333 2.22387 15.3333 2.54531V13.4546C15.3333 13.7761 15.2056 14.0844 14.9783 14.3117C14.751 14.539 14.4427 14.6666 14.1213 14.6666H3.21194C2.54261 14.6666 1.99994 14.124 1.99994 13.4546V2.54531C1.99994 2.22387 2.12763 1.91559 2.35493 1.6883ZM3.33327 6.66665V9.33331H5.99994V6.66665H3.33327ZM3.33327 10.6666V13.3333H5.99994V10.6666H3.33327ZM7.33327 13.3333H9.99994V10.6666H7.33327V13.3333ZM11.3333 13.3333H13.9999V10.6666H11.3333V13.3333ZM13.9999 9.33331V6.66665H11.3333V9.33331H13.9999ZM9.99994 6.66665H7.33327V9.33331H9.99994V6.66665ZM13.9999 2.66665H3.33327V5.33331H13.9999V2.66665Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 864 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.66667 1.99996V14H8V12H9.33334V14.6666C9.33334 15.0348 9.03486 15.3333 8.66667 15.3333H2C1.63181 15.3333 1.33334 15.0348 1.33334 14.6666V1.33329C1.33334 0.965103 1.63181 0.666626 2 0.666626H8.66667C9.03486 0.666626 9.33334 0.965103 9.33334 1.33329V3.99996H8V1.99996H2.66667Z" fill="#646A73"/>
|
||||
<path d="M12.2482 8.69455L7 8.69455C6.95623 8.69455 6.91288 8.68593 6.87244 8.66918C6.832 8.65243 6.79525 8.62788 6.7643 8.59692C6.73335 8.56597 6.7088 8.52922 6.69204 8.48878C6.67529 8.44834 6.66667 8.40499 6.66667 8.36122L6.66667 7.69455C6.66667 7.65078 6.67529 7.60744 6.69204 7.56699C6.7088 7.52655 6.73335 7.48981 6.7643 7.45885C6.79525 7.4279 6.832 7.40335 6.87244 7.38659C6.91288 7.36984 6.95623 7.36122 7 7.36122L12.2569 7.36122L10.6026 5.70689C10.5401 5.64438 10.505 5.55961 10.505 5.47122C10.505 5.38283 10.5401 5.29806 10.6026 5.23556L11.0742 4.76422C11.1367 4.70173 11.2215 4.66663 11.3099 4.66663C11.3983 4.66663 11.4831 4.70173 11.5456 4.76422L14.5689 7.78756C14.5999 7.81851 14.6245 7.85528 14.6413 7.89574C14.658 7.93621 14.6667 7.97958 14.6667 8.02339C14.6667 8.06719 14.658 8.11057 14.6413 8.15103C14.6245 8.1915 14.5999 8.22826 14.5689 8.25922L11.5456 11.2829C11.4831 11.3454 11.3983 11.3805 11.3099 11.3805C11.2215 11.3805 11.1367 11.3454 11.0742 11.2829L10.6026 10.8116C10.5401 10.749 10.505 10.6643 10.505 10.5759C10.505 10.4875 10.5401 10.4027 10.6026 10.3402L12.2482 8.69455Z" fill="#646A73"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -1,4 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.17896 1.78057L14.8914 6.05213C15.0972 6.18312 15.0972 6.48359 14.8914 6.61457L8.17896 10.8861C8.06977 10.9556 7.93023 10.9556 7.82104 10.8861L1.10858 6.61457C0.902751 6.48359 0.90275 6.18312 1.10858 6.05213L7.82104 1.78057C7.93023 1.71108 8.06977 1.71108 8.17896 1.78057ZM3.15017 6.33335L8 9.41961L12.8498 6.33335L8 3.2471L3.15017 6.33335Z" fill="#1F2329"/>
|
||||
<path d="M1.73096 9.25743C1.57401 9.1612 1.36878 9.21042 1.27255 9.36736L0.924074 9.9357C0.827845 10.0926 0.877063 10.2979 1.03401 10.3941L7.13744 14.1364C7.56569 14.399 8.10095 14.399 8.52919 14.1364L14.6326 10.3941C14.7896 10.2979 14.8388 10.0926 14.7426 9.9357L14.3941 9.36736C14.2979 9.21042 14.0926 9.1612 13.9357 9.25743L7.92044 12.9457C7.86698 12.9784 7.79965 12.9784 7.7462 12.9457L1.73096 9.25743Z" fill="#1F2329"/>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.17896 1.78057L14.8914 6.05213C15.0972 6.18312 15.0972 6.48359 14.8914 6.61457L8.17896 10.8861C8.06977 10.9556 7.93023 10.9556 7.82104 10.8861L1.10858 6.61457C0.902751 6.48359 0.90275 6.18312 1.10858 6.05213L7.82104 1.78057C7.93023 1.71108 8.06977 1.71108 8.17896 1.78057ZM3.15017 6.33335L8 9.41961L12.8498 6.33335L8 3.2471L3.15017 6.33335Z"/>
|
||||
<path d="M1.73096 9.25743C1.57401 9.1612 1.36878 9.21042 1.27255 9.36736L0.924074 9.9357C0.827845 10.0926 0.877063 10.2979 1.03401 10.3941L7.13744 14.1364C7.56569 14.399 8.10095 14.399 8.52919 14.1364L14.6326 10.3941C14.7896 10.2979 14.8388 10.0926 14.7426 9.9357L14.3941 9.36736C14.2979 9.21042 14.0926 9.1612 13.9357 9.25743L7.92044 12.9457C7.86698 12.9784 7.79965 12.9784 7.7462 12.9457L1.73096 9.25743Z"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 898 B After Width: | Height: | Size: 856 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.13909 6L4.07322 1.93414C3.97559 1.83651 3.97559 1.67822 4.07322 1.58059L4.42678 1.22703C4.52441 1.1294 4.6827 1.1294 4.78033 1.22703L9.19975 5.64645C9.39501 5.84171 9.39501 6.15829 9.19975 6.35356L4.78033 10.773C4.6827 10.8706 4.52441 10.8706 4.42678 10.773L4.07322 10.4194C3.97559 10.3218 3.97559 10.1635 4.07322 10.0659L8.13909 6Z" fill="#8F959E"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 466 B |
@@ -0,0 +1,11 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1127_1532)">
|
||||
<path d="M14 8.00009C14 4.68642 11.3137 2.00009 8.00001 2.00009C6.48001 2.00009 5.09234 2.56509 4.03501 3.49676L3.35734 2.32342C4.66614 1.24984 6.30722 0.664246 8.00001 0.666756C12.05 0.666756 15.3333 3.95009 15.3333 8.00009C15.3333 9.35876 14.964 10.6311 14.32 11.7221L13.5247 10.3448L13.527 10.3391L12.179 8.00009H14ZM3.09667 4.54076L4.14534 6.33509H2.23401C2.07829 6.87637 1.99952 7.43686 2.00001 8.00009C2.00001 11.3138 4.68634 14.0001 8.00001 14.0001C9.93234 14.0001 11.6513 13.0868 12.749 11.6678L13.4587 12.8971C12.116 14.3928 10.168 15.3334 8.00001 15.3334C3.95001 15.3334 0.666672 12.0501 0.666672 8.00009C0.664084 6.27817 1.26996 4.6107 2.37734 3.29209L3.09767 4.53976L3.09667 4.54076Z" fill="#646A73"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.13642 4.66663C7.86709 4.66663 7.62309 4.82329 7.51442 5.06663L5.13775 10.4C5.10232 10.4804 5.08327 10.5672 5.08172 10.6551C5.08017 10.743 5.09616 10.8304 5.12874 10.912C5.16132 10.9937 5.20985 11.0681 5.27149 11.1308C5.33314 11.1935 5.40665 11.2433 5.48775 11.2773C5.65213 11.3487 5.83795 11.3527 6.00526 11.2885C6.17257 11.2243 6.30799 11.097 6.38242 10.934L6.94709 9.66663H9.32642L9.89109 10.9333C9.96539 11.0963 10.1007 11.2236 10.2678 11.2879C10.435 11.3522 10.6207 11.3484 10.7851 11.2773C10.8663 11.2433 10.9399 11.1935 11.0016 11.1307C11.0633 11.068 11.1118 10.9935 11.1444 10.9118C11.177 10.83 11.193 10.7426 11.1914 10.6546C11.1898 10.5666 11.1706 10.4798 11.1351 10.3993L8.75842 5.06596C8.65042 4.82329 8.40642 4.66663 8.13642 4.66663ZM7.54109 8.33329L8.13642 6.99663L8.73242 8.33329H7.54109Z" fill="#646A73"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1127_1532">
|
||||
<rect width="16" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -1,11 +1,12 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import Menu from './Menu.vue'
|
||||
import Workspace from './Workspace.vue'
|
||||
import Person from './Person.vue'
|
||||
import LOGO from '@/assets/LOGO.svg'
|
||||
import LOGO_fold from '@/assets/LOGO-fold.svg'
|
||||
import icon_moments_categories_outlined from '@/assets/svg/icon_moments-categories_outlined.svg'
|
||||
import icon_side_fold_outlined from '@/assets/svg/icon_side-fold_outlined.svg'
|
||||
import Default_avatar from '@/assets/svg/icon-member-default.svg'
|
||||
import icon_side_expand_outlined from '@/assets/svg/icon_side-expand_outlined.svg'
|
||||
const collapse = ref(false)
|
||||
|
||||
@@ -19,6 +20,7 @@ const handleFoldExpand = () => {
|
||||
<div class="left-side" :class="collapse && 'left-side-collapse'">
|
||||
<LOGO_fold v-if="collapse" style="margin: 0 0 6px 5px"></LOGO_fold>
|
||||
<LOGO v-else style="margin-bottom: 6px"></LOGO>
|
||||
<Workspace :collapse="collapse"></Workspace>
|
||||
<Menu :collapse="collapse"></Menu>
|
||||
<div class="bottom">
|
||||
<div class="back-to_workspace">
|
||||
@@ -28,12 +30,8 @@ const handleFoldExpand = () => {
|
||||
{{ collapse ? '' : '返回工作空间' }}
|
||||
</div>
|
||||
<div class="personal-info">
|
||||
<el-icon size="32" class="default-avatar">
|
||||
<Default_avatar></Default_avatar>
|
||||
</el-icon>
|
||||
|
||||
{{ collapse ? '' : '飞小致' }}
|
||||
<el-icon @click="handleFoldExpand" size="20" class="fold">
|
||||
<Person :collapse="collapse"></Person>
|
||||
<el-icon size="20" class="fold" @click="handleFoldExpand">
|
||||
<icon_side_expand_outlined v-if="collapse"></icon_side_expand_outlined>
|
||||
<icon_side_fold_outlined v-else></icon_side_fold_outlined>
|
||||
</el-icon>
|
||||
@@ -97,10 +95,6 @@ const handleFoldExpand = () => {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 16px;
|
||||
.default-avatar {
|
||||
margin-right: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.fold {
|
||||
cursor: pointer;
|
||||
@@ -153,6 +147,10 @@ const handleFoldExpand = () => {
|
||||
box-shadow: 0px 2px 4px 0px #1f23291f;
|
||||
background-color: #fff;
|
||||
border-radius: 12px;
|
||||
|
||||
&:has(.no-padding) {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ const activeIndex = computed(() => {
|
||||
height: 40px;
|
||||
border-radius: 6px;
|
||||
&.is-active.is-active.is-active {
|
||||
background-color: #1CBA901A;
|
||||
background-color: #1cba901a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,11 @@ import { h } from 'vue'
|
||||
import { ElMenuItem, ElSubMenu, ElIcon } from 'element-plus-secondary'
|
||||
|
||||
import model from '@/assets/svg/icon_dataset_filled.svg'
|
||||
import ds from '@/assets/svg/ds.svg'
|
||||
|
||||
const iconMap = {
|
||||
icon_ai: model,
|
||||
ds,
|
||||
} as { [key: string]: any }
|
||||
|
||||
const titleWithIcon = (props: any) => {
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import Default_avatar from '@/assets/svg/icon-member-default.svg'
|
||||
import icon_admin_outlined from '@/assets/svg/icon_admin_outlined.svg'
|
||||
import icon_maybe_outlined from '@/assets/svg/icon-maybe_outlined.svg'
|
||||
import icon_key_outlined from '@/assets/svg/icon-key_outlined.svg'
|
||||
import icon_translate_outlined from '@/assets/svg/icon_translate_outlined.svg'
|
||||
import icon_logout_outlined from '@/assets/svg/icon_logout_outlined.svg'
|
||||
import icon_right_outlined from '@/assets/svg/icon_right_outlined.svg'
|
||||
import icon_done_outlined from '@/assets/svg/icon_done_outlined.svg'
|
||||
|
||||
defineProps({
|
||||
collapse: { type: [Boolean], required: true },
|
||||
})
|
||||
|
||||
const name = ref('飞小致')
|
||||
const currentLanguage = ref('zh-CN')
|
||||
const emit = defineEmits(['selectPerson'])
|
||||
const languageList = [
|
||||
{
|
||||
name: 'English',
|
||||
value: 'en',
|
||||
},
|
||||
{
|
||||
name: '简体中文',
|
||||
value: 'zh-CN',
|
||||
},
|
||||
{
|
||||
name: '繁體中文',
|
||||
value: 'zh-CN',
|
||||
},
|
||||
]
|
||||
const handlePersonChange = () => {
|
||||
emit('selectPerson')
|
||||
}
|
||||
|
||||
const handleDefaultLanguageChange = (item: any) => {
|
||||
currentLanguage.value = item.value
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-popover popper-class="system-person" :placement="collapse ? 'right' : 'top'">
|
||||
<template #reference>
|
||||
<button class="person" :class="collapse && 'collapse'">
|
||||
<el-icon size="32" class="default-avatar">
|
||||
<Default_avatar></Default_avatar>
|
||||
</el-icon>
|
||||
<span v-if="!collapse" class="name">{{ name }}</span>
|
||||
</button></template
|
||||
>
|
||||
<div class="popover">
|
||||
<div class="popover-content">
|
||||
<div class="info">
|
||||
<el-icon style="transform: scale(1.25)" size="32">
|
||||
<Default_avatar></Default_avatar>
|
||||
</el-icon>
|
||||
<div class="top">{{ name }}</div>
|
||||
<div class="bottom">feixaozhi</div>
|
||||
</div>
|
||||
<div class="popover-item" @click="handlePersonChange">
|
||||
<el-icon size="16">
|
||||
<icon_admin_outlined></icon_admin_outlined>
|
||||
</el-icon>
|
||||
<div class="datasource-name">系统管理</div>
|
||||
</div>
|
||||
<div class="popover-item">
|
||||
<el-icon size="16">
|
||||
<icon_key_outlined></icon_key_outlined>
|
||||
</el-icon>
|
||||
<div class="datasource-name">修改密码</div>
|
||||
</div>
|
||||
<el-popover :teleported="false" popper-class="system-language" placement="right">
|
||||
<template #reference>
|
||||
<div class="popover-item">
|
||||
<el-icon size="16">
|
||||
<icon_translate_outlined></icon_translate_outlined>
|
||||
</el-icon>
|
||||
<div class="datasource-name">语言</div>
|
||||
<el-icon style="transform: scale(1.33)" class="right" size="16">
|
||||
<icon_right_outlined></icon_right_outlined>
|
||||
</el-icon>
|
||||
</div>
|
||||
</template>
|
||||
<div class="language-popover">
|
||||
<div
|
||||
v-for="ele in languageList"
|
||||
:key="ele.name"
|
||||
class="popover-item_language"
|
||||
:class="currentLanguage === ele.value && 'isActive'"
|
||||
@click="handleDefaultLanguageChange(ele)"
|
||||
>
|
||||
<div class="language-name">{{ ele.name }}</div>
|
||||
<el-icon size="16" class="done">
|
||||
<icon_done_outlined></icon_done_outlined>
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</el-popover>
|
||||
|
||||
<div class="popover-item">
|
||||
<el-icon size="16">
|
||||
<icon_maybe_outlined></icon_maybe_outlined>
|
||||
</el-icon>
|
||||
<div class="datasource-name">帮助</div>
|
||||
</div>
|
||||
<div class="popover-item mr4">
|
||||
<el-icon size="16">
|
||||
<icon_logout_outlined></icon_logout_outlined>
|
||||
</el-icon>
|
||||
<div class="datasource-name">退出登录</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-popover>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.person {
|
||||
border-radius: 6px;
|
||||
padding: 0 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
width: 156px;
|
||||
height: 40px;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
|
||||
&.collapse {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:active {
|
||||
background: #1f23291a;
|
||||
border: 1px solid #1f23291a;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: #1f232926;
|
||||
border: 1px solid #1f23291a;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less">
|
||||
.system-person.system-person {
|
||||
padding: 0;
|
||||
width: 200px !important;
|
||||
box-shadow: 0px 4px 8px 0px #1f23291a;
|
||||
border: 1px solid #dee0e3;
|
||||
position: relative;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 40px;
|
||||
left: 0;
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
background: #dee0e3;
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 62px;
|
||||
left: 0;
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
background: #dee0e3;
|
||||
}
|
||||
|
||||
.popover {
|
||||
.info {
|
||||
height: 62px;
|
||||
padding: 8px;
|
||||
|
||||
.ed-icon {
|
||||
margin: 6px 8px 0 7px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.top {
|
||||
float: left;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
width: calc(100% - 60px);
|
||||
}
|
||||
|
||||
.bottom {
|
||||
float: left;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
width: calc(100% - 60px);
|
||||
}
|
||||
}
|
||||
.popover-item {
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 12px;
|
||||
padding-right: 8px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
.datasource-name {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
&.mr4 {
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.right {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.system-language.system-language {
|
||||
padding: 4px 4px 2px 4px;
|
||||
width: 240px !important;
|
||||
box-shadow: 0px 4px 8px 0px #1f23291a;
|
||||
border: 1px solid #dee0e3;
|
||||
|
||||
.language-popover {
|
||||
.popover-item_language {
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
margin-bottom: 2px;
|
||||
position: relative;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
&:not(.empty):hover {
|
||||
background: #1f23291a;
|
||||
}
|
||||
|
||||
.language-name {
|
||||
margin-left: 8px;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.done {
|
||||
margin-left: auto;
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.isActive {
|
||||
color: var(--ed-color-primary);
|
||||
|
||||
.done {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,195 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import icon_expand_down_filled from '@/assets/svg/icon_expand-down_filled.svg'
|
||||
import icon_moments_categories_outlined from '@/assets/svg/icon_moments-categories_outlined.svg'
|
||||
import icon_done_outlined from '@/assets/svg/icon_done_outlined.svg'
|
||||
import icon_searchOutline_outlined from '@/assets/svg/icon_search-outline_outlined.svg'
|
||||
|
||||
defineProps({
|
||||
collapse: { type: [Boolean], required: true },
|
||||
})
|
||||
|
||||
const name = ref('默认工作空间')
|
||||
const currentWorkspace = ref('默认工作空间')
|
||||
const defaultDatasourceList = ref([
|
||||
{
|
||||
name: '默认工作空间',
|
||||
},
|
||||
] as any[])
|
||||
const workspaceKeywords = ref('')
|
||||
const defaultWorkspaceListWithSearch = computed(() => {
|
||||
if (!workspaceKeywords.value) return defaultDatasourceList.value
|
||||
return defaultDatasourceList.value.filter((ele) =>
|
||||
ele.name.toLowerCase().includes(workspaceKeywords.value.toLowerCase())
|
||||
)
|
||||
})
|
||||
const formatKeywords = (item: string) => {
|
||||
if (!workspaceKeywords.value) return item
|
||||
return item.replaceAll(
|
||||
workspaceKeywords.value,
|
||||
`<span class="isSearch">${workspaceKeywords.value}</span>`
|
||||
)
|
||||
}
|
||||
|
||||
const emit = defineEmits(['selectWorkspace'])
|
||||
|
||||
const handleDefaultWorkspaceChange = (item: any) => {
|
||||
currentWorkspace.value = item.name
|
||||
emit('selectWorkspace', item)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-popover popper-class="system-workspace" :placement="collapse ? 'right' : 'bottom'">
|
||||
<template #reference>
|
||||
<button class="workspace" :class="collapse && 'collapse'">
|
||||
<el-icon size="16">
|
||||
<icon_moments_categories_outlined></icon_moments_categories_outlined>
|
||||
</el-icon>
|
||||
<span v-if="!collapse" class="name">{{ name }}</span>
|
||||
<el-icon v-if="!collapse" style="transform: scale(0.5)" class="expand" size="24">
|
||||
<icon_expand_down_filled></icon_expand_down_filled>
|
||||
</el-icon></button
|
||||
></template>
|
||||
<div class="popover">
|
||||
<el-input
|
||||
v-model="workspaceKeywords"
|
||||
clearable
|
||||
style="width: 100%; margin-right: 12px"
|
||||
placeholder="通过名称搜索"
|
||||
>
|
||||
<template #prefix>
|
||||
<el-icon>
|
||||
<icon_searchOutline_outlined class="svg-icon" />
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
<div class="popover-content">
|
||||
<div
|
||||
v-for="ele in defaultWorkspaceListWithSearch"
|
||||
:key="ele.name"
|
||||
class="popover-item"
|
||||
:class="currentWorkspace === ele.name && 'isActive'"
|
||||
@click="handleDefaultWorkspaceChange(ele)"
|
||||
>
|
||||
<el-icon size="16">
|
||||
<icon_moments_categories_outlined></icon_moments_categories_outlined>
|
||||
</el-icon>
|
||||
<div class="datasource-name" v-html="formatKeywords(ele.name)"></div>
|
||||
<el-icon size="16" class="done">
|
||||
<icon_done_outlined></icon_done_outlined>
|
||||
</el-icon>
|
||||
</div>
|
||||
<div v-if="!defaultWorkspaceListWithSearch.length" class="popover-item empty">
|
||||
没有找到相关结果
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-popover>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.workspace {
|
||||
background: #1f23290a;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #1f23291a;
|
||||
padding: 0 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
width: 208px;
|
||||
height: 40px;
|
||||
margin-bottom: 12px;
|
||||
|
||||
&.collapse {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.expand {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: #1f23291a;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: #1f232926;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less">
|
||||
.system-workspace.system-workspace {
|
||||
padding: 4px 0;
|
||||
width: 280px !important;
|
||||
box-shadow: 0px 4px 8px 0px #1f23291a;
|
||||
border: 1px solid #dee0e3;
|
||||
.ed-input {
|
||||
.ed-input__wrapper {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
border-bottom: 1px solid #1f232926;
|
||||
}
|
||||
|
||||
.popover {
|
||||
.popover-content {
|
||||
padding: 4px;
|
||||
}
|
||||
.popover-item {
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 12px;
|
||||
padding-right: 8px;
|
||||
margin-bottom: 2px;
|
||||
position: relative;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
&:not(.empty):hover {
|
||||
background: #1f23291a;
|
||||
}
|
||||
|
||||
&.empty {
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
color: #8f959e;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.datasource-name {
|
||||
margin-left: 8px;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.done {
|
||||
margin-left: auto;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.isSearch {
|
||||
color: var(--ed-color-primary);
|
||||
}
|
||||
|
||||
&.isActive {
|
||||
color: var(--ed-color-primary);
|
||||
|
||||
.done {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -5,7 +5,7 @@ import LayoutDsl from '@/components/layout/LayoutDsl.vue'
|
||||
import SinglePage from '@/components/layout/SinglePage.vue'
|
||||
import login from '@/views/login/index.vue'
|
||||
import chat from '@/views/chat/index.vue'
|
||||
import ds from '@/views/ds/index.vue'
|
||||
import Datasource from '@/views/ds/Datasource.vue'
|
||||
import DashboardEditor from '@/views/dashboard/editor/index.vue'
|
||||
import DashboardPreview from '@//views/dashboard/preview/SQPreviewSingle.vue'
|
||||
import Dashboard from '@/views/dashboard/index.vue'
|
||||
@@ -48,13 +48,13 @@ const router = createRouter({
|
||||
},
|
||||
{
|
||||
path: '/ds',
|
||||
component: Layout,
|
||||
component: LayoutDsl,
|
||||
redirect: '/ds/index',
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
name: 'ds',
|
||||
component: ds,
|
||||
component: Datasource,
|
||||
meta: { title: 'Data Connections', icon: 'ds' },
|
||||
},
|
||||
],
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
<script lang="ts" setup>
|
||||
import azure from '@/assets/model/icon_Azure_OpenAI_colorful.png'
|
||||
import delIcon from '@/assets/svg/icon_delete.svg'
|
||||
import icon_more_outlined from '@/assets/svg/icon_more_outlined.svg'
|
||||
import icon_form_outlined from '@/assets/svg/icon_form_outlined.svg'
|
||||
import icon_chat_outlined from '@/assets/svg/icon_chat_outlined.svg'
|
||||
|
||||
import edit from '@/assets/svg/icon_edit_outlined.svg'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
name: string
|
||||
type: string
|
||||
rate?: string
|
||||
description?: string
|
||||
id?: string
|
||||
}>(),
|
||||
{
|
||||
name: '-',
|
||||
type: '-',
|
||||
rate: '-',
|
||||
description: '-',
|
||||
id: '-',
|
||||
}
|
||||
)
|
||||
|
||||
const emits = defineEmits(['edit', 'del', 'question', 'dataTableDetail'])
|
||||
|
||||
const handleEdit = () => {
|
||||
emits('edit')
|
||||
}
|
||||
|
||||
const handleDel = () => {
|
||||
emits('del', props.id)
|
||||
}
|
||||
|
||||
const handleQuestion = () => {
|
||||
emits('question', props.id)
|
||||
}
|
||||
|
||||
const dataTableDetail = () => {
|
||||
emits('dataTableDetail')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card" @click="dataTableDetail">
|
||||
<div class="name-icon">
|
||||
<img :src="azure" width="32px" height="32px" />
|
||||
<div class="info">
|
||||
<div class="name">{{ name }}</div>
|
||||
<div class="type">{{ type }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="type-value">
|
||||
{{ description }}
|
||||
</div>
|
||||
|
||||
<div class="bottom-info">
|
||||
<div class="form-rate">
|
||||
<el-icon class="form-icon" size="16">
|
||||
<icon_form_outlined></icon_form_outlined>
|
||||
</el-icon>
|
||||
{{ rate }}
|
||||
</div>
|
||||
<div class="methods">
|
||||
<el-button type="primary" style="margin-right: 8px" @click.stop="handleQuestion">
|
||||
<el-icon style="margin-right: 2px" size="12">
|
||||
<icon_chat_outlined></icon_chat_outlined>
|
||||
</el-icon>
|
||||
开启问数
|
||||
</el-button>
|
||||
<el-popover click.stop :teleported="false" popper-class="popover-card" placement="bottom">
|
||||
<template #reference>
|
||||
<el-icon class="more" size="16"> <icon_more_outlined></icon_more_outlined> </el-icon
|
||||
></template>
|
||||
|
||||
<div class="content">
|
||||
<div class="item" @click.stop="handleEdit">
|
||||
<el-icon size="16">
|
||||
<edit></edit>
|
||||
</el-icon>
|
||||
编辑
|
||||
</div>
|
||||
<div class="item" @click.stop="handleDel">
|
||||
<el-icon size="16">
|
||||
<delIcon></delIcon>
|
||||
</el-icon>
|
||||
删除
|
||||
</div>
|
||||
</div>
|
||||
</el-popover>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.card {
|
||||
width: 370px;
|
||||
border: 1px solid #dee0e3;
|
||||
padding: 16px;
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
margin: 0 16px 16px 0;
|
||||
&:hover {
|
||||
box-shadow: 0px 6px 24px 0px #1f232914;
|
||||
}
|
||||
|
||||
.name-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
|
||||
.info {
|
||||
margin-left: 12px;
|
||||
.name {
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
}
|
||||
.type {
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
color: #646a73;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.type-value {
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
color: #646a73;
|
||||
height: 44px;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.bottom-info {
|
||||
margin-top: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 28px;
|
||||
|
||||
.ed-button {
|
||||
height: 28px;
|
||||
min-width: 78px;
|
||||
}
|
||||
|
||||
.form-rate {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #646a73;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
|
||||
.form-icon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.methods {
|
||||
align-items: center;
|
||||
display: none;
|
||||
|
||||
.more {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
background-color: #1f23291a;
|
||||
position: absolute;
|
||||
border-radius: 6px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
transform: translate(-50%, -50%);
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&::after {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
.methods {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less">
|
||||
.popover-card.popover-card.popover-card {
|
||||
box-shadow: 0px 4px 8px 0px #1f23291a;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #dee0e3;
|
||||
width: 120px !important;
|
||||
min-width: 120px !important;
|
||||
padding: 0;
|
||||
.content {
|
||||
position: relative;
|
||||
&::after {
|
||||
position: absolute;
|
||||
content: '';
|
||||
top: 40px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background: #dee0e3;
|
||||
}
|
||||
.item {
|
||||
position: relative;
|
||||
padding-left: 12px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
.ed-icon {
|
||||
margin-right: 8px;
|
||||
color: #646a73;
|
||||
}
|
||||
&:hover {
|
||||
&::after {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
width: 112px;
|
||||
height: 32px;
|
||||
border-radius: 4px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: #1f23291a;
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,475 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { datasourceApi } from '@/api/datasource'
|
||||
import icon_right_outlined from '@/assets/svg/icon_right_outlined.svg'
|
||||
import icon_form_outlined from '@/assets/svg/icon_form_outlined.svg'
|
||||
import icon_searchOutline_outlined from '@/assets/svg/icon_search-outline_outlined.svg'
|
||||
import EmptyBackground from '@/views/dashboard/common/EmptyBackground.vue'
|
||||
import edit from '@/assets/svg/icon_edit_outlined.svg'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import ParamsForm from './ParamsForm.vue'
|
||||
|
||||
interface Table {
|
||||
name: string
|
||||
host: string
|
||||
port: string
|
||||
username: string
|
||||
password: string
|
||||
database: string
|
||||
extraJdbc: string
|
||||
dbSchema: string
|
||||
filename: string
|
||||
sheets: string
|
||||
mode: string
|
||||
timeout: string
|
||||
configuration: string
|
||||
id: number
|
||||
}
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
info: Table
|
||||
}>(),
|
||||
{
|
||||
info: () => ({
|
||||
name: '-',
|
||||
host: '-',
|
||||
port: '-',
|
||||
username: '-',
|
||||
password: '-',
|
||||
database: '-',
|
||||
extraJdbc: '-',
|
||||
dbSchema: '-',
|
||||
filename: '-',
|
||||
sheets: '-',
|
||||
mode: '-',
|
||||
timeout: '-',
|
||||
configuration: '-',
|
||||
id: 0,
|
||||
}),
|
||||
}
|
||||
)
|
||||
const { t } = useI18n()
|
||||
const paramsFormRef = ref()
|
||||
const tableList = ref([] as any[])
|
||||
const keywords = ref('')
|
||||
const tableListWithSearch = computed(() => {
|
||||
if (!keywords.value) return tableList.value
|
||||
return tableList.value.filter((ele) =>
|
||||
ele.name.toLowerCase().includes(keywords.value.toLowerCase())
|
||||
)
|
||||
})
|
||||
|
||||
const currentTable = ref<any>({})
|
||||
const ds = ref<any>({})
|
||||
const btnSelect = ref('q')
|
||||
|
||||
const init = () => {
|
||||
datasourceApi.getDs(props.info.id).then((res) => {
|
||||
ds.value = res
|
||||
fieldList.value = []
|
||||
datasourceApi.tableList(props.info.id).then((res) => {
|
||||
tableList.value = res
|
||||
})
|
||||
})
|
||||
}
|
||||
onMounted(() => {
|
||||
init()
|
||||
})
|
||||
const tableComment = ref('')
|
||||
const fieldDialog = ref<boolean>(false)
|
||||
const tableDialog = ref<boolean>(false)
|
||||
const fieldComment = ref('')
|
||||
const currentField = ref<any>({})
|
||||
const previewData = ref<any>({})
|
||||
const fieldList = ref<any>([])
|
||||
|
||||
const buildData = () => {
|
||||
return { table: currentTable.value, fields: fieldList.value }
|
||||
}
|
||||
|
||||
const handleSelectTableList = () => {
|
||||
paramsFormRef.value.open(props.info)
|
||||
}
|
||||
|
||||
const clickTable = (table: any) => {
|
||||
currentTable.value = table
|
||||
datasourceApi.fieldList(table.id).then((res) => {
|
||||
fieldList.value = res
|
||||
datasourceApi.previewData(props.info.id, buildData()).then((res) => {
|
||||
previewData.value = res
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const closeTable = () => {
|
||||
tableDialog.value = false
|
||||
}
|
||||
const editTable = () => {
|
||||
tableComment.value = currentTable.value.custom_comment
|
||||
tableDialog.value = true
|
||||
}
|
||||
const saveTable = () => {
|
||||
currentTable.value.custom_comment = tableComment.value
|
||||
datasourceApi.saveTable(currentTable.value).then(() => {
|
||||
closeTable()
|
||||
ElMessage({
|
||||
message: t('common.save_success'),
|
||||
type: 'success',
|
||||
showClose: true,
|
||||
})
|
||||
})
|
||||
}
|
||||
const closeField = () => {
|
||||
fieldDialog.value = false
|
||||
}
|
||||
|
||||
const refresh = () => {
|
||||
datasourceApi.tableList(props.info.id).then((res) => {
|
||||
tableList.value = res
|
||||
if (!currentTable.value.table_name) return
|
||||
const nameArr = tableList.value.map((ele: any) => ele.table_name)
|
||||
if (!nameArr.includes(currentTable.value.table_name)) {
|
||||
currentTable.value = {}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const saveField = () => {
|
||||
currentField.value.custom_comment = fieldComment.value
|
||||
datasourceApi.saveField(currentField.value).then(() => {
|
||||
closeField()
|
||||
ElMessage({
|
||||
message: t('common.save_success'),
|
||||
type: 'success',
|
||||
showClose: true,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const editField = (row: any) => {
|
||||
currentField.value = row
|
||||
fieldComment.value = currentField.value.custom_comment
|
||||
fieldDialog.value = true
|
||||
}
|
||||
|
||||
const changeStatus = (row: any) => {
|
||||
currentField.value = row
|
||||
datasourceApi.saveField(currentField.value).then(() => {
|
||||
closeField()
|
||||
ElMessage({
|
||||
message: t('common.save_success'),
|
||||
type: 'success',
|
||||
showClose: true,
|
||||
})
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="data-table no-padding">
|
||||
<div class="info">
|
||||
数据源
|
||||
<el-icon size="12">
|
||||
<icon_right_outlined></icon_right_outlined>
|
||||
</el-icon>
|
||||
<div class="name">{{ info.name }}</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="side-list">
|
||||
<div class="select-table_top">
|
||||
数据表
|
||||
<el-icon size="18" @click="handleSelectTableList">
|
||||
<icon_form_outlined></icon_form_outlined>
|
||||
</el-icon>
|
||||
</div>
|
||||
<el-input v-model="keywords" clearable style="width: 232px" placeholder="搜索">
|
||||
<template #prefix>
|
||||
<el-icon>
|
||||
<icon_searchOutline_outlined class="svg-icon" />
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
|
||||
<div class="list-content">
|
||||
<div
|
||||
v-for="ele in tableListWithSearch"
|
||||
:key="ele.table_name"
|
||||
class="model"
|
||||
:class="currentTable.table_name === ele.table_name && 'isActive'"
|
||||
@click="clickTable(ele)"
|
||||
>
|
||||
<el-icon size="16">
|
||||
<icon_form_outlined></icon_form_outlined>
|
||||
</el-icon>
|
||||
<span class="name">{{ ele.table_name }}</span>
|
||||
</div>
|
||||
<EmptyBackground
|
||||
v-if="!!keywords && !tableListWithSearch.length"
|
||||
:description="'没有找到相关内容'"
|
||||
img-type="tree"
|
||||
style="width: 100%; margin-top: 100px"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="currentTable.table_name" class="info-table">
|
||||
<div class="table-name">
|
||||
<div class="name">{{ currentTable.table_name }}</div>
|
||||
<div class="notes">
|
||||
{{ currentTable.custom_comment }}
|
||||
<el-icon style="margin-left: 8px; cursor: pointer" size="16" @click="editTable">
|
||||
<edit></edit>
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-content">
|
||||
<div class="btn-select">
|
||||
<el-button :class="[btnSelect === 'd' && 'is-active']" text @click="btnSelect = 'd'">
|
||||
{{ t('ds.table_schema') }}
|
||||
</el-button>
|
||||
<el-button :class="[btnSelect === 'q' && 'is-active']" text @click="btnSelect = 'q'">
|
||||
{{ t('ds.preview') }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<div class="preview-or-schema">
|
||||
<el-table v-if="btnSelect === 'd'" :data="fieldList" style="width: 100%">
|
||||
<el-table-column prop="field_name" :label="t('ds.field.name')" width="180" />
|
||||
<el-table-column prop="field_type" :label="t('ds.field.type')" width="180" />
|
||||
<el-table-column prop="field_comment" :label="t('ds.field.comment')" />
|
||||
<el-table-column :label="t('ds.field.custom_comment')">
|
||||
<template #default="scope">
|
||||
<div class="field-comment">
|
||||
<span>{{ scope.row.custom_comment }}</span>
|
||||
<el-tooltip :offset="14" effect="dark" content="编辑" placement="top">
|
||||
<el-icon class="action-btn" size="16" @click="editField(scope.row)">
|
||||
<edit></edit>
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('ds.field.status')" width="180">
|
||||
<template #default="scope">
|
||||
<div style="display: flex; align-items: center">
|
||||
<el-switch
|
||||
v-model="scope.row.checked"
|
||||
size="small"
|
||||
@change="changeStatus(scope.row)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-table v-else :data="previewData.data" style="width: 100%; height: 600px">
|
||||
<el-table-column
|
||||
v-for="(c, index) in previewData.fields"
|
||||
:key="index"
|
||||
:prop="c"
|
||||
:label="c"
|
||||
/>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-dialog
|
||||
v-model="tableDialog"
|
||||
:title="t('ds.edit.table_comment')"
|
||||
width="600"
|
||||
:destroy-on-close="true"
|
||||
:close-on-click-modal="false"
|
||||
@closed="closeTable"
|
||||
>
|
||||
<el-input v-model="tableComment" :rows="3" type="textarea" />
|
||||
<div style="display: flex; justify-content: flex-end; margin-top: 20px">
|
||||
<el-button @click="closeTable">{{ t('common.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="saveTable">{{ t('common.confirm') }}</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
v-model="fieldDialog"
|
||||
:title="t('ds.edit.field_comment')"
|
||||
width="600"
|
||||
:destroy-on-close="true"
|
||||
:close-on-click-modal="false"
|
||||
@closed="closeField"
|
||||
>
|
||||
<el-input v-model="fieldComment" :rows="3" type="textarea" />
|
||||
<div style="display: flex; justify-content: flex-end; margin-top: 20px">
|
||||
<el-button @click="closeField">{{ t('common.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="saveField">{{ t('common.confirm') }}</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<ParamsForm ref="paramsFormRef" @refresh="refresh"></ParamsForm>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.data-table {
|
||||
height: 100%;
|
||||
.info {
|
||||
height: 56px;
|
||||
width: 100%;
|
||||
padding-left: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-family: PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
color: #646a73;
|
||||
border-bottom: 1px solid #1f232926;
|
||||
|
||||
.name {
|
||||
color: #1f2329;
|
||||
}
|
||||
}
|
||||
.content {
|
||||
height: calc(100% - 56px);
|
||||
position: relative;
|
||||
.side-list {
|
||||
width: 280px;
|
||||
padding: 8px 16px;
|
||||
height: 100%;
|
||||
border-right: 1px solid #1f232926;
|
||||
.select-table_top {
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 8px;
|
||||
|
||||
.ed-icon:hover {
|
||||
cursor: pointer;
|
||||
color: var(--ed-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.ed-input {
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
.list-content {
|
||||
height: calc(100% - 100px);
|
||||
.model {
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 8px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
.name {
|
||||
margin-left: 8px;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
}
|
||||
&:hover {
|
||||
background: #1f23291a;
|
||||
}
|
||||
|
||||
&.isActive {
|
||||
background: #1cba901a;
|
||||
color: var(--ed-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.info-table {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: calc(100% - 280px);
|
||||
.table-name {
|
||||
height: 80px;
|
||||
padding: 16px 0 0 24px;
|
||||
border-bottom: 1px solid #1f232926;
|
||||
|
||||
.name {
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.notes {
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
color: #646a73;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.table-content {
|
||||
padding: 16px 24px;
|
||||
|
||||
.btn-select {
|
||||
height: 32px;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
display: inline-flex;
|
||||
background: #ffffff;
|
||||
align-items: center;
|
||||
border: 1px solid #d0d3d6;
|
||||
border-radius: 4px;
|
||||
|
||||
.is-active {
|
||||
background: #1cba901a;
|
||||
}
|
||||
|
||||
.ed-button:not(.is-active) {
|
||||
color: #1f2329;
|
||||
}
|
||||
.ed-button.is-text {
|
||||
height: 24px;
|
||||
width: auto;
|
||||
padding: 0 8px;
|
||||
line-height: 24px;
|
||||
}
|
||||
.ed-button + .ed-button {
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.preview-or-schema {
|
||||
margin-top: 16px;
|
||||
|
||||
.field-comment {
|
||||
height: 24px;
|
||||
}
|
||||
.ed-icon {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
margin-top: 4px;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
background-color: #1f23291a;
|
||||
position: absolute;
|
||||
border-radius: 6px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
transform: translate(-50%, -50%);
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&::after {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,386 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, shallowRef, nextTick } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus-secondary'
|
||||
import icon_searchOutline_outlined from '@/assets/svg/icon_search-outline_outlined.svg'
|
||||
import arrow_down from '@/assets/svg/arrow-down.svg'
|
||||
import icon_add_outlined from '@/assets/svg/icon_add_outlined.svg'
|
||||
import EmptyBackground from '@/views/dashboard/common/EmptyBackground.vue'
|
||||
import DataTable from './DataTable.vue'
|
||||
import icon_done_outlined from '@/assets/svg/icon_done_outlined.svg'
|
||||
import icon_close_outlined from '@/assets/svg/operate/ope-close.svg'
|
||||
import DatasourceList from './DatasourceList.vue'
|
||||
import DatasourceListSide from './DatasourceListSide.vue'
|
||||
import DatasourceForm from './DatasourceForm.vue'
|
||||
import { datasourceApi } from '@/api/datasource'
|
||||
import Card from './Card.vue'
|
||||
import { dsTypeWithImg } from './js/ds-type'
|
||||
|
||||
interface Datasource {
|
||||
name: string
|
||||
type: string
|
||||
img: string
|
||||
rate?: string
|
||||
id?: string
|
||||
}
|
||||
|
||||
const keywords = ref('')
|
||||
const defaultDatasourceKeywords = ref('')
|
||||
const datasourceConfigvVisible = ref(false)
|
||||
const editDatasource = ref(false)
|
||||
const activeStep = ref(0)
|
||||
const activeName = ref('')
|
||||
const datasourceFormRef = ref()
|
||||
|
||||
const datasourceList = shallowRef([] as Datasource[])
|
||||
const defaultDatasourceList = shallowRef(dsTypeWithImg as (Datasource & { img: string })[])
|
||||
|
||||
const currentDefaultDatasource = ref('')
|
||||
const datasourceListWithSearch = computed(() => {
|
||||
if (!keywords.value) return datasourceList.value
|
||||
return datasourceList.value.filter((ele) =>
|
||||
ele.name.toLowerCase().includes(keywords.value.toLowerCase())
|
||||
)
|
||||
})
|
||||
const beforeClose = () => {
|
||||
datasourceConfigvVisible.value = false
|
||||
}
|
||||
const defaultDatasourceListWithSearch = computed(() => {
|
||||
if (!defaultDatasourceKeywords.value) return defaultDatasourceList.value
|
||||
return defaultDatasourceList.value.filter((ele) =>
|
||||
ele.name.toLowerCase().includes(defaultDatasourceKeywords.value.toLowerCase())
|
||||
)
|
||||
})
|
||||
|
||||
const handleDefaultDatasourceChange = (item: any) => {
|
||||
currentDefaultDatasource.value = item.name
|
||||
}
|
||||
|
||||
const formatKeywords = (item: string) => {
|
||||
if (!defaultDatasourceKeywords.value) return item
|
||||
return item.replaceAll(
|
||||
defaultDatasourceKeywords.value,
|
||||
`<span class="isSearch">${defaultDatasourceKeywords.value}</span>`
|
||||
)
|
||||
}
|
||||
|
||||
const handleEditDatasource = (res: any) => {
|
||||
activeStep.value = 1
|
||||
datasourceConfigvVisible.value = true
|
||||
editDatasource.value = true
|
||||
nextTick(() => {
|
||||
datasourceFormRef.value.initForm(res)
|
||||
})
|
||||
}
|
||||
|
||||
const handleAddDatasource = () => {
|
||||
editDatasource.value = false
|
||||
datasourceConfigvVisible.value = true
|
||||
}
|
||||
|
||||
const deleteHandler = (id: any) => {
|
||||
ElMessageBox.confirm('Are you sure to delete?', 'Warning', {
|
||||
confirmButtonText: 'OK',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
datasourceApi.delete(id).then(() => {
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: 'Delete completed',
|
||||
})
|
||||
search()
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage({
|
||||
type: 'info',
|
||||
message: 'Delete canceled',
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const clickDatasource = (ele: any) => {
|
||||
activeStep.value = 1
|
||||
activeName.value = ele.name
|
||||
}
|
||||
|
||||
const clickDatasourceSide = (ele: any) => {
|
||||
activeName.value = ele.name
|
||||
activeStep.value = 1
|
||||
}
|
||||
|
||||
const search = () => {
|
||||
datasourceApi.list().then((res: any) => {
|
||||
datasourceList.value = res
|
||||
})
|
||||
}
|
||||
search()
|
||||
|
||||
const submit = (item: any) => {
|
||||
if (!item.id) {
|
||||
datasourceApi.add(item).then(() => {
|
||||
beforeClose()
|
||||
search()
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: 'Add completed',
|
||||
})
|
||||
})
|
||||
return
|
||||
}
|
||||
datasourceApi.edit(item).then(() => {
|
||||
beforeClose()
|
||||
search()
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: 'Edit completed',
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const currentDataTable = ref()
|
||||
const dataTableDetail = (ele: any) => {
|
||||
currentDataTable.value = ele
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-show="!currentDataTable" class="datasource-config">
|
||||
<div class="datasource-methods">
|
||||
<span class="title">数据源</span>
|
||||
<div class="button-input">
|
||||
<el-input
|
||||
v-model="keywords"
|
||||
clearable
|
||||
style="width: 240px; margin-right: 12px"
|
||||
placeholder="搜索"
|
||||
>
|
||||
<template #prefix>
|
||||
<el-icon>
|
||||
<icon_searchOutline_outlined class="svg-icon" />
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
|
||||
<el-popover popper-class="system-default_datasource" placement="bottom">
|
||||
<template #reference>
|
||||
<el-button secondary>
|
||||
全部类型
|
||||
<el-icon style="margin-left: 8px">
|
||||
<arrow_down></arrow_down>
|
||||
</el-icon> </el-button
|
||||
></template>
|
||||
<div class="popover">
|
||||
<el-input
|
||||
v-model="defaultDatasourceKeywords"
|
||||
clearable
|
||||
style="width: 100%; margin-right: 12px"
|
||||
placeholder="通过名称搜索"
|
||||
>
|
||||
<template #prefix>
|
||||
<el-icon>
|
||||
<icon_searchOutline_outlined class="svg-icon" />
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
<div class="popover-content">
|
||||
<div
|
||||
v-for="ele in defaultDatasourceListWithSearch"
|
||||
:key="ele.name"
|
||||
class="popover-item"
|
||||
:class="currentDefaultDatasource === ele.name && 'isActive'"
|
||||
@click="handleDefaultDatasourceChange(ele)"
|
||||
>
|
||||
<img :src="ele.img" width="24px" height="24px" />
|
||||
<div class="datasource-name" v-html="formatKeywords(ele.name)"></div>
|
||||
<el-icon size="16" class="done">
|
||||
<icon_done_outlined></icon_done_outlined>
|
||||
</el-icon>
|
||||
</div>
|
||||
<div v-if="!defaultDatasourceListWithSearch.length" class="popover-item empty">
|
||||
没有找到相关结果
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-popover>
|
||||
|
||||
<el-button type="primary" @click="handleAddDatasource">
|
||||
<template #icon>
|
||||
<icon_add_outlined></icon_add_outlined>
|
||||
</template>
|
||||
新建数据源
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<EmptyBackground
|
||||
v-if="!!keywords && !datasourceListWithSearch.length"
|
||||
:description="'没有找到相关内容'"
|
||||
img-type="tree"
|
||||
/>
|
||||
|
||||
<div v-else class="card-content">
|
||||
<Card
|
||||
v-for="ele in datasourceListWithSearch"
|
||||
:id="ele.id"
|
||||
:key="ele.id"
|
||||
:name="ele.name"
|
||||
:type="ele.type"
|
||||
:rate="ele.rate"
|
||||
@edit="handleEditDatasource(ele)"
|
||||
@del="deleteHandler"
|
||||
@data-table-detail="dataTableDetail(ele)"
|
||||
></Card>
|
||||
</div>
|
||||
<el-drawer
|
||||
v-model="datasourceConfigvVisible"
|
||||
:close-on-click-modal="false"
|
||||
size="calc(100% - 100px)"
|
||||
modal-class="datasource-drawer-fullscreen"
|
||||
direction="btt"
|
||||
:before-close="beforeClose"
|
||||
:show-close="false"
|
||||
>
|
||||
<template #header="{ close }">
|
||||
<span style="white-space: nowrap">新建数据源</span>
|
||||
<div v-if="!editDatasource" class="flex-center" style="width: 100%">
|
||||
<el-steps custom style="max-width: 500px; flex: 1" :active="activeStep" align-center>
|
||||
<el-step>
|
||||
<template #title> 选择数据源 </template>
|
||||
</el-step>
|
||||
<el-step>
|
||||
<template #title> 配置信息 </template>
|
||||
</el-step>
|
||||
<el-step>
|
||||
<template #title> 选择数据表 </template>
|
||||
</el-step>
|
||||
</el-steps>
|
||||
</div>
|
||||
<el-icon style="cursor: pointer" @click="close">
|
||||
<icon_close_outlined></icon_close_outlined>
|
||||
</el-icon>
|
||||
</template>
|
||||
<DatasourceList v-if="activeStep === 0" @click-datasource="clickDatasource"></DatasourceList>
|
||||
<DatasourceListSide
|
||||
v-if="activeStep === 1 && !editDatasource"
|
||||
:active-name="activeName"
|
||||
@click-datasource="clickDatasourceSide"
|
||||
></DatasourceListSide>
|
||||
<DatasourceForm
|
||||
v-if="[1, 2].includes(activeStep)"
|
||||
ref="datasourceFormRef"
|
||||
:active-step="activeStep"
|
||||
:active-name="activeName"
|
||||
@submit="submit"
|
||||
@change-active-step="(val: number) => (activeStep = val)"
|
||||
></DatasourceForm>
|
||||
</el-drawer>
|
||||
</div>
|
||||
<DataTable v-if="currentDataTable" :info="currentDataTable"></DataTable>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.datasource-config {
|
||||
height: calc(100% - 16px);
|
||||
.datasource-methods {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 16px;
|
||||
.title {
|
||||
font-weight: 500;
|
||||
font-size: 20px;
|
||||
line-height: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
.card-content {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less">
|
||||
.system-default_datasource.system-default_datasource {
|
||||
padding: 4px 0;
|
||||
width: 325px !important;
|
||||
box-shadow: 0px 4px 8px 0px #1f23291a;
|
||||
border: 1px solid #dee0e3;
|
||||
.ed-input {
|
||||
.ed-input__wrapper {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
border-bottom: 1px solid #1f232926;
|
||||
}
|
||||
|
||||
.popover {
|
||||
.popover-content {
|
||||
padding: 4px;
|
||||
}
|
||||
.popover-item {
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 12px;
|
||||
padding-right: 8px;
|
||||
margin-bottom: 2px;
|
||||
position: relative;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
&:not(.empty):hover {
|
||||
background: #1f23291a;
|
||||
}
|
||||
|
||||
&.empty {
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
color: #8f959e;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.datasource-name {
|
||||
margin-left: 8px;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.done {
|
||||
margin-left: auto;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.isSearch {
|
||||
color: var(--ed-color-primary);
|
||||
}
|
||||
|
||||
&.isActive {
|
||||
color: var(--ed-color-primary);
|
||||
|
||||
.done {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.datasource-drawer-fullscreen {
|
||||
.ed-drawer__body {
|
||||
padding: 0;
|
||||
}
|
||||
.is-process .ed-step__line {
|
||||
background-color: var(--ed-color-primary);
|
||||
}
|
||||
}
|
||||
.confirm-no_icon {
|
||||
border-radius: 12px;
|
||||
padding: 24px;
|
||||
.tip {
|
||||
margin-top: 24px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,667 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, onMounted, computed, watch, nextTick } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { datasourceApi } from '@/api/datasource'
|
||||
import icon_searchOutline_outlined from '@/assets/svg/icon_search-outline_outlined.svg'
|
||||
import { encrypted, decrypted } from './js/aes'
|
||||
import { ElMessage } from 'element-plus-secondary'
|
||||
import type { FormInstance, FormRules } from 'element-plus-secondary'
|
||||
import icon_form_outlined from '@/assets/svg/icon_form_outlined.svg'
|
||||
import FixedSizeList from 'element-plus-secondary/es/components/virtual-list/src/components/fixed-size-list.mjs'
|
||||
import { Plus } from '@element-plus/icons-vue'
|
||||
import { useCache } from '@/utils/useCache'
|
||||
import { dsType, haveSchema } from '@/views/ds/js/ds-type'
|
||||
import EmptyBackground from '@/views/dashboard/common/EmptyBackground.vue'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
activeName: string
|
||||
activeStep: number
|
||||
}>(),
|
||||
{
|
||||
activeName: '',
|
||||
activeStep: 0,
|
||||
}
|
||||
)
|
||||
|
||||
const { wsCache } = useCache()
|
||||
const dsFormRef = ref<FormInstance>()
|
||||
const emit = defineEmits(['refresh', 'submit', 'changeActiveStep'])
|
||||
const isCreate = ref(true)
|
||||
const isEditTable = ref(false)
|
||||
const checkList = ref<any>([])
|
||||
const tableList = ref<any>([])
|
||||
const excelUploadSuccess = ref(false)
|
||||
const tableListLoading = ref(false)
|
||||
const token = wsCache.get('user.token')
|
||||
const headers = ref<any>({ 'X-SQLBOT-TOKEN': `Bearer ${token}` })
|
||||
const dialogTitle = ref('')
|
||||
const getUploadURL = import.meta.env.VITE_API_BASE_URL + '/datasource/uploadExcel'
|
||||
const saveLoading = ref<boolean>(false)
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const rules = reactive<FormRules>({
|
||||
name: [
|
||||
{ required: true, message: t('ds.form.validate.name_required'), trigger: 'blur' },
|
||||
{ min: 1, max: 50, message: t('ds.form.validate.name_length'), trigger: 'blur' },
|
||||
],
|
||||
type: [{ required: true, message: t('ds.form.validate.type_required'), trigger: 'change' }],
|
||||
host: [{ required: true, message: 'Please input host', trigger: 'blur' }],
|
||||
port: [{ required: true, message: 'Please input port', trigger: 'blur' }],
|
||||
database: [{ required: true, message: 'Please input database', trigger: 'blur' }],
|
||||
mode: [{ required: true, message: 'Please choose mode', trigger: 'change' }],
|
||||
dbSchema: [{ required: true, message: 'Please input schema', trigger: 'blur' }],
|
||||
})
|
||||
|
||||
const dialogVisible = ref<boolean>(false)
|
||||
const form = ref<any>({
|
||||
name: '',
|
||||
description: '',
|
||||
type: 'mysql',
|
||||
configuration: '',
|
||||
driver: '',
|
||||
host: '',
|
||||
port: 0,
|
||||
username: '',
|
||||
password: '',
|
||||
database: '',
|
||||
extraJdbc: '',
|
||||
dbSchema: '',
|
||||
filename: '',
|
||||
sheets: [],
|
||||
mode: 'service_name',
|
||||
timeout: 30,
|
||||
})
|
||||
|
||||
const close = () => {
|
||||
dialogVisible.value = false
|
||||
isCreate.value = true
|
||||
emit('changeActiveStep', 0)
|
||||
isEditTable.value = false
|
||||
checkList.value = []
|
||||
tableList.value = []
|
||||
excelUploadSuccess.value = false
|
||||
saveLoading.value = false
|
||||
}
|
||||
|
||||
const initForm = (item: any, editTable: boolean = false) => {
|
||||
isEditTable.value = false
|
||||
dsFormRef.value!.clearValidate()
|
||||
console.log('item', item)
|
||||
|
||||
if (item) {
|
||||
dialogTitle.value = editTable ? t('ds.form.title.choose_tables') : t('ds.form.title.edit')
|
||||
isCreate.value = false
|
||||
form.value.id = item.id
|
||||
form.value.name = item.name
|
||||
form.value.description = item.description
|
||||
form.value.type = item.type
|
||||
form.value.configuration = item.configuration
|
||||
if (item.configuration) {
|
||||
const configuration = JSON.parse(decrypted(item.configuration))
|
||||
form.value.host = configuration.host
|
||||
form.value.port = configuration.port
|
||||
form.value.username = configuration.username
|
||||
form.value.password = configuration.password
|
||||
form.value.database = configuration.database
|
||||
form.value.extraJdbc = configuration.extraJdbc
|
||||
form.value.dbSchema = configuration.dbSchema
|
||||
form.value.filename = configuration.filename
|
||||
form.value.sheets = configuration.sheets
|
||||
form.value.mode = configuration.mode
|
||||
form.value.timeout = configuration.timeout ? configuration.timeout : 30
|
||||
}
|
||||
|
||||
if (editTable) {
|
||||
dialogTitle.value = t('ds.form.choose_tables')
|
||||
emit('changeActiveStep', 2)
|
||||
isEditTable.value = true
|
||||
isCreate.value = false
|
||||
// request tables and check tables
|
||||
|
||||
datasourceApi.tableList(item.id).then((res: any) => {
|
||||
checkList.value = res.map((ele: any) => {
|
||||
return ele.table_name
|
||||
})
|
||||
if (item.type === 'excel') {
|
||||
tableList.value = form.value.sheets
|
||||
nextTick(() => {
|
||||
handleCheckedTablesChange([...checkList.value])
|
||||
})
|
||||
} else {
|
||||
tableListLoading.value = true
|
||||
const requestObj = buildConf()
|
||||
datasourceApi
|
||||
.getTablesByConf(requestObj)
|
||||
.then((table) => {
|
||||
tableList.value = table
|
||||
checkList.value = checkList.value.filter((ele: string) => {
|
||||
return table
|
||||
.map((ele: any) => {
|
||||
return ele.tableName
|
||||
})
|
||||
.includes(ele)
|
||||
})
|
||||
nextTick(() => {
|
||||
handleCheckedTablesChange([...checkList.value])
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
tableListLoading.value = false
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
dialogTitle.value = t('ds.form.title.add')
|
||||
isCreate.value = true
|
||||
isEditTable.value = false
|
||||
checkList.value = []
|
||||
tableList.value = []
|
||||
form.value = {
|
||||
name: '',
|
||||
description: '',
|
||||
type: 'mysql',
|
||||
configuration: '',
|
||||
driver: '',
|
||||
host: '',
|
||||
port: 0,
|
||||
username: '',
|
||||
password: '',
|
||||
database: '',
|
||||
extraJdbc: '',
|
||||
dbSchema: '',
|
||||
filename: '',
|
||||
sheets: [],
|
||||
mode: 'service_name',
|
||||
timeout: 30,
|
||||
}
|
||||
}
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const save = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
await formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
saveLoading.value = true
|
||||
const list = tableList.value
|
||||
.filter((ele: any) => {
|
||||
return checkList.value.includes(ele.tableName)
|
||||
})
|
||||
.map((ele: any) => {
|
||||
return { table_name: ele.tableName, table_comment: ele.tableComment }
|
||||
})
|
||||
|
||||
const requestObj = buildConf()
|
||||
if (form.value.id) {
|
||||
if (!isEditTable.value) {
|
||||
// only update datasource config info
|
||||
datasourceApi.update(requestObj).then((res) => {
|
||||
console.log(res)
|
||||
close()
|
||||
emit('refresh')
|
||||
})
|
||||
} else {
|
||||
// save table and field
|
||||
datasourceApi.chooseTables(form.value.id, list).then(() => {
|
||||
close()
|
||||
emit('refresh')
|
||||
})
|
||||
}
|
||||
} else {
|
||||
requestObj.tables = list
|
||||
datasourceApi.add(requestObj).then((res: any) => {
|
||||
console.log(res)
|
||||
close()
|
||||
emit('refresh')
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const buildConf = () => {
|
||||
form.value.configuration = encrypted(
|
||||
JSON.stringify({
|
||||
host: form.value.host,
|
||||
port: form.value.port,
|
||||
username: form.value.username,
|
||||
password: form.value.password,
|
||||
database: form.value.database,
|
||||
extraJdbc: form.value.extraJdbc,
|
||||
dbSchema: form.value.dbSchema,
|
||||
filename: form.value.filename,
|
||||
sheets: form.value.sheets,
|
||||
mode: form.value.mode,
|
||||
timeout: form.value.timeout,
|
||||
})
|
||||
)
|
||||
const obj = JSON.parse(JSON.stringify(form.value))
|
||||
delete obj.driver
|
||||
delete obj.host
|
||||
delete obj.port
|
||||
delete obj.username
|
||||
delete obj.password
|
||||
delete obj.database
|
||||
delete obj.extraJdbc
|
||||
delete obj.dbSchema
|
||||
delete obj.filename
|
||||
delete obj.sheets
|
||||
delete obj.mode
|
||||
delete obj.timeout
|
||||
return obj
|
||||
}
|
||||
|
||||
const check = () => {
|
||||
const requestObj = buildConf()
|
||||
datasourceApi.check(requestObj).then((res: any) => {
|
||||
if (res) {
|
||||
ElMessage({
|
||||
message: t('ds.form.connect.success'),
|
||||
type: 'success',
|
||||
showClose: true,
|
||||
})
|
||||
} else {
|
||||
ElMessage({
|
||||
message: t('ds.form.connect.failed'),
|
||||
type: 'error',
|
||||
showClose: true,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const next = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
await formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
if (form.value.type === 'excel') {
|
||||
// next, show tables
|
||||
if (excelUploadSuccess.value) {
|
||||
emit('changeActiveStep', props.activeStep + 1)
|
||||
}
|
||||
} else {
|
||||
// check status if success do next
|
||||
const requestObj = buildConf()
|
||||
datasourceApi.check(requestObj).then((res: boolean) => {
|
||||
if (res) {
|
||||
emit('changeActiveStep', props.activeStep + 1)
|
||||
// request tables
|
||||
datasourceApi.getTablesByConf(requestObj).then((res: any) => {
|
||||
tableList.value = res
|
||||
})
|
||||
} else {
|
||||
ElMessage({
|
||||
message: t('ds.form.connect.failed'),
|
||||
type: 'error',
|
||||
showClose: true,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const preview = () => {
|
||||
emit('changeActiveStep', props.activeStep - 1)
|
||||
}
|
||||
|
||||
const beforeUpload = (rawFile: any) => {
|
||||
if (rawFile.size / 1024 / 1024 > 50) {
|
||||
ElMessage.error('File size can not exceed 50MB!')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
const onSuccess = (response: any) => {
|
||||
form.value.filename = response.data.filename
|
||||
form.value.sheets = response.data.sheets
|
||||
tableList.value = response.data.sheets
|
||||
excelUploadSuccess.value = true
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
setTimeout(() => {
|
||||
dsFormRef.value!.clearValidate()
|
||||
}, 100)
|
||||
})
|
||||
|
||||
const submitModle = () => {
|
||||
dsFormRef.value!.validate((res: any) => {
|
||||
if (res) {
|
||||
emit('submit')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const keywords = ref('')
|
||||
const tableListWithSearch = computed(() => {
|
||||
if (!keywords.value) return tableList.value
|
||||
return tableList.value.filter((ele: any) =>
|
||||
ele.tableName.toLowerCase().includes(keywords.value.toLowerCase())
|
||||
)
|
||||
})
|
||||
|
||||
watch(keywords, () => {
|
||||
const tableNameArr = tableListWithSearch.value.map((ele: any) => ele.tableName)
|
||||
checkList.value = checkTableList.filter((ele) => tableNameArr.includes(ele))
|
||||
const checkedCount = checkList.value.length
|
||||
checkAll.value = checkedCount === tableListWithSearch.value.length
|
||||
isIndeterminate.value = checkedCount > 0 && checkedCount < tableListWithSearch.value.length
|
||||
})
|
||||
|
||||
const checkAll = ref(false)
|
||||
const isIndeterminate = ref(false)
|
||||
let checkTableList = [] as any[]
|
||||
|
||||
const handleCheckAllChange = (val: any) => {
|
||||
checkList.value = val
|
||||
? [
|
||||
...new Set([
|
||||
...tableListWithSearch.value.map((ele: any) => ele.tableName),
|
||||
...checkList.value,
|
||||
]),
|
||||
]
|
||||
: []
|
||||
isIndeterminate.value = false
|
||||
const tableNameArr = tableListWithSearch.value.map((ele: any) => ele.tableName)
|
||||
checkTableList = val
|
||||
? [...new Set([...tableNameArr, ...checkTableList])]
|
||||
: checkTableList.filter((ele) => !tableNameArr.includes(ele))
|
||||
}
|
||||
|
||||
const handleCheckedTablesChange = (value: any[]) => {
|
||||
const checkedCount = value.length
|
||||
checkAll.value = checkedCount === tableListWithSearch.value.length
|
||||
isIndeterminate.value = checkedCount > 0 && checkedCount < tableListWithSearch.value.length
|
||||
const tableNameArr = tableListWithSearch.value.map((ele: any) => ele.tableName)
|
||||
checkTableList = [
|
||||
...new Set([...checkTableList.filter((ele) => !tableNameArr.includes(ele)), ...value]),
|
||||
]
|
||||
}
|
||||
|
||||
const tableListSave = () => {
|
||||
save(dsFormRef.value)
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initForm,
|
||||
submitModle,
|
||||
tableListSave,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="model-form" :class="(!isCreate || activeStep === 2) && 'edit-form'">
|
||||
<div v-if="isCreate && activeStep !== 2" class="model-name">{{ activeName }}</div>
|
||||
|
||||
<div class="form-content">
|
||||
<el-form
|
||||
v-show="activeStep === 1"
|
||||
ref="dsFormRef"
|
||||
:model="form"
|
||||
label-position="top"
|
||||
label-width="auto"
|
||||
:rules="rules"
|
||||
>
|
||||
<el-form-item :label="t('ds.form.name')" prop="name">
|
||||
<el-input v-model="form.name" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('ds.form.description')">
|
||||
<el-input v-model="form.description" :rows="2" type="textarea" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('ds.type')" prop="type">
|
||||
<el-select v-model="form.type" placeholder="Select Type" :disabled="!isCreate">
|
||||
<el-option
|
||||
v-for="item in dsType"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<div v-if="form.type === 'excel'">
|
||||
<el-form-item label="File">
|
||||
<el-upload
|
||||
:disabled="!isCreate"
|
||||
accept=".xls, .xlsx, .csv"
|
||||
:headers="headers"
|
||||
:action="getUploadURL"
|
||||
:before-upload="beforeUpload"
|
||||
:on-success="onSuccess"
|
||||
>
|
||||
<el-button :disabled="!isCreate">{{ t('ds.form.upload.button') }}</el-button>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">{{ t('ds.form.upload.tip') }}</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div v-else>
|
||||
<el-form-item :label="t('ds.form.host')" prop="host">
|
||||
<el-input v-model="form.host" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('ds.form.port')" prop="port">
|
||||
<el-input v-model="form.port" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('ds.form.username')">
|
||||
<el-input v-model="form.username" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('ds.form.password')">
|
||||
<el-input v-model="form.password" type="password" show-password />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('ds.form.database')" prop="database">
|
||||
<el-input v-model="form.database" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="form.type === 'oracle'"
|
||||
:label="t('ds.form.connect_mode')"
|
||||
prop="mode"
|
||||
>
|
||||
<el-radio-group v-model="form.mode">
|
||||
<el-radio value="service_name">{{ t('ds.form.mode.service_name') }}</el-radio>
|
||||
<el-radio value="sid">{{ t('ds.form.mode.sid') }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('ds.form.extra_jdbc')">
|
||||
<el-input v-model="form.extraJdbc" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="haveSchema.includes(form.type)"
|
||||
:label="t('ds.form.schema')"
|
||||
prop="dbSchema"
|
||||
>
|
||||
<el-input v-model="form.dbSchema" />
|
||||
<el-button v-if="false" link type="primary" :icon="Plus">Get Schema</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('ds.form.timeout')" prop="timeout">
|
||||
<el-input-number v-model="form.timeout" :min="0" :max="300" controls-position="right" />
|
||||
</el-form-item>
|
||||
<span>
|
||||
<span>{{ t('ds.form.support_version') }}: </span>
|
||||
<span v-if="form.type === 'sqlServer'">2012+</span>
|
||||
<span v-else-if="form.type === 'oracle'">12+</span>
|
||||
<span v-else-if="form.type === 'mysql'">5.6+</span>
|
||||
<span v-else-if="form.type === 'pg'">9.6+</span>
|
||||
</span>
|
||||
</div>
|
||||
</el-form>
|
||||
<div v-show="activeStep === 2" v-loading="tableListLoading" class="select-data_table">
|
||||
<div class="title">选择数据表 ({{ checkList.length }}/ {{ tableList.length }})</div>
|
||||
<el-input
|
||||
v-model="keywords"
|
||||
clearable
|
||||
style="width: 100%; margin-bottom: 16px"
|
||||
placeholder="搜索"
|
||||
>
|
||||
<template #prefix>
|
||||
<el-icon>
|
||||
<icon_searchOutline_outlined class="svg-icon" />
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
<div class="container">
|
||||
<div class="select-all">
|
||||
<el-checkbox
|
||||
v-model="checkAll"
|
||||
:indeterminate="isIndeterminate"
|
||||
@change="handleCheckAllChange"
|
||||
>
|
||||
全选
|
||||
</el-checkbox>
|
||||
</div>
|
||||
<EmptyBackground
|
||||
v-if="!!keywords && !tableListWithSearch.length"
|
||||
:description="'没有找到相关内容'"
|
||||
img-type="tree"
|
||||
style="width: 100%"
|
||||
/>
|
||||
<el-checkbox-group
|
||||
v-else
|
||||
v-model="checkList"
|
||||
style="position: relative"
|
||||
@change="handleCheckedTablesChange"
|
||||
>
|
||||
<FixedSizeList
|
||||
:item-size="32"
|
||||
:data="tableListWithSearch"
|
||||
:total="tableListWithSearch.length"
|
||||
:width="800"
|
||||
:height="460"
|
||||
:scrollbar-always-on="true"
|
||||
class-name="ed-select-dropdown__list"
|
||||
layout="vertical"
|
||||
>
|
||||
<template #default="{ index, style }">
|
||||
<div class="list-item_primary" :style="style">
|
||||
<el-checkbox :label="tableListWithSearch[index].tableName">
|
||||
<el-icon size="16" style="margin-right: 8px">
|
||||
<icon_form_outlined></icon_form_outlined>
|
||||
</el-icon>
|
||||
{{ tableListWithSearch[index].tableName }}</el-checkbox
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
</FixedSizeList>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="draw-foot">
|
||||
<el-button @click="close">{{ t('common.cancel') }}</el-button>
|
||||
<el-button v-show="!isCreate && !isEditTable && form.type !== 'excel'" @click="check">
|
||||
{{ t('ds.test_connection') }}
|
||||
</el-button>
|
||||
<el-button v-show="activeStep === 1 && isCreate" type="primary" @click="next(dsFormRef)">
|
||||
{{ t('common.next') }}
|
||||
</el-button>
|
||||
<el-button v-show="activeStep === 2 && isCreate" @click="preview">
|
||||
{{ t('ds.previous') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
v-show="activeStep === 2 || !isCreate"
|
||||
:loading="saveLoading"
|
||||
type="primary"
|
||||
@click="save(dsFormRef)"
|
||||
>
|
||||
{{ t('common.save') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.model-form {
|
||||
width: calc(100% - 280px);
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 56px;
|
||||
height: 100%;
|
||||
padding-bottom: 120px;
|
||||
overflow-y: auto;
|
||||
.model-name {
|
||||
height: 56px;
|
||||
width: 100%;
|
||||
padding-left: 24px;
|
||||
border-bottom: 1px solid #1f232926;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.form-content {
|
||||
width: 800px;
|
||||
margin: 0 auto;
|
||||
padding-top: 24px;
|
||||
|
||||
.ed-form-item--default {
|
||||
margin-bottom: 16px;
|
||||
|
||||
&.is-error {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.draw-foot) {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: calc(100% - 280px);
|
||||
height: 64px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
border-top: 1px solid #1f232926;
|
||||
padding-right: 24px;
|
||||
background-color: #fff;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
&.edit-form {
|
||||
width: 100%;
|
||||
|
||||
:deep(.draw-foot) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
.select-data_table {
|
||||
.title {
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
margin: 0 0 16px 0;
|
||||
}
|
||||
.container {
|
||||
height: 524px;
|
||||
overflow-y: auto;
|
||||
border: 1px solid #dee0e3;
|
||||
border-radius: 4px;
|
||||
|
||||
.select-all {
|
||||
background: #f5f6f7;
|
||||
height: 40px;
|
||||
padding-left: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #dee0e3;
|
||||
}
|
||||
|
||||
:deep(.ed-checkbox__label) {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
:deep(.ed-vl__window) {
|
||||
scrollbar-width: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,109 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, shallowRef, computed } from 'vue'
|
||||
import icon_searchOutline_outlined from '@/assets/svg/icon_search-outline_outlined.svg'
|
||||
import EmptyBackground from '@/views/dashboard/common/EmptyBackground.vue'
|
||||
import { dsTypeWithImg } from './js/ds-type'
|
||||
|
||||
interface Datasource {
|
||||
name: string
|
||||
type: string
|
||||
img: string
|
||||
rate?: string
|
||||
id?: string
|
||||
}
|
||||
const keywords = ref('')
|
||||
const modelList = shallowRef(dsTypeWithImg as Datasource[])
|
||||
const modelListWithSearch = computed(() => {
|
||||
if (!keywords.value) return modelList.value
|
||||
return modelList.value.filter((ele) =>
|
||||
ele.name.toLowerCase().includes(keywords.value.toLowerCase())
|
||||
)
|
||||
})
|
||||
const emits = defineEmits(['clickDatasource'])
|
||||
const handleModelClick = (item: any) => {
|
||||
emits('clickDatasource', item)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="model-list">
|
||||
<div class="title">选择数据源</div>
|
||||
<el-input
|
||||
v-model="keywords"
|
||||
clearable
|
||||
style="width: 100%; margin-right: 12px"
|
||||
placeholder="搜索"
|
||||
>
|
||||
<template #prefix>
|
||||
<el-icon>
|
||||
<icon_searchOutline_outlined class="svg-icon" />
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
<div class="list-content">
|
||||
<div
|
||||
v-for="ele in modelListWithSearch"
|
||||
:key="ele.name"
|
||||
class="model"
|
||||
@click="handleModelClick(ele)"
|
||||
>
|
||||
<img width="32px" height="32px" :src="ele.img" />
|
||||
<span class="name">{{ ele.name }}</span>
|
||||
</div>
|
||||
<EmptyBackground
|
||||
v-if="!!keywords && !modelListWithSearch.length"
|
||||
:description="'没有找到相关内容'"
|
||||
img-type="tree"
|
||||
style="width: 100%; margin-top: 100px"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.model-list {
|
||||
width: 800px;
|
||||
margin: 0 auto;
|
||||
max-height: 100%;
|
||||
padding-top: 24px;
|
||||
.title {
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.list-content {
|
||||
margin-top: 16px;
|
||||
display: flex;
|
||||
height: calc(100% - 40px);
|
||||
flex-wrap: wrap;
|
||||
|
||||
.model:nth-child(odd) {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.model {
|
||||
width: 392px;
|
||||
height: 64px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 16px;
|
||||
margin-bottom: 16px;
|
||||
border: 1px solid #dee0e3;
|
||||
border-radius: 4px;
|
||||
margin-left: 16px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
box-shadow: 0px 6px 24px 0px #1f232914;
|
||||
}
|
||||
.name {
|
||||
margin-left: 12px;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,106 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, shallowRef, computed } from 'vue'
|
||||
import icon_searchOutline_outlined from '@/assets/svg/icon_search-outline_outlined.svg'
|
||||
import EmptyBackground from '@/views/dashboard/common/EmptyBackground.vue'
|
||||
import { dsTypeWithImg } from './js/ds-type'
|
||||
|
||||
interface Datasource {
|
||||
name: string
|
||||
type: string
|
||||
img: string
|
||||
rate?: string
|
||||
id?: string
|
||||
}
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
activeName: string
|
||||
}>(),
|
||||
{
|
||||
activeName: '',
|
||||
}
|
||||
)
|
||||
const keywords = ref('')
|
||||
const modelList = shallowRef(dsTypeWithImg as Datasource[])
|
||||
const modelListWithSearch = computed(() => {
|
||||
if (!keywords.value) return modelList.value
|
||||
return modelList.value.filter((ele) =>
|
||||
ele.name.toLowerCase().includes(keywords.value.toLowerCase())
|
||||
)
|
||||
})
|
||||
const emits = defineEmits(['clickDatasource'])
|
||||
const handleModelClick = (item: any) => {
|
||||
emits('clickDatasource', item)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="model-list_side">
|
||||
<el-input
|
||||
v-model="keywords"
|
||||
clearable
|
||||
style="width: 232px; margin: 16px 0 8px 24px"
|
||||
placeholder="搜索"
|
||||
>
|
||||
<template #prefix>
|
||||
<el-icon>
|
||||
<icon_searchOutline_outlined class="svg-icon" />
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
<div class="list-content">
|
||||
<div
|
||||
v-for="ele in modelListWithSearch"
|
||||
:key="ele.name"
|
||||
class="model"
|
||||
:class="activeName === ele.name && 'isActive'"
|
||||
@click="handleModelClick(ele)"
|
||||
>
|
||||
<img width="20px" height="20px" :src="ele.img" />
|
||||
<span class="name">{{ ele.name }}</span>
|
||||
</div>
|
||||
<EmptyBackground
|
||||
v-if="!!keywords && !modelListWithSearch.length"
|
||||
:description="'没有找到相关内容'"
|
||||
img-type="tree"
|
||||
style="width: 100%; margin-top: 100px"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.model-list_side {
|
||||
width: 280px;
|
||||
height: 100%;
|
||||
border-right: 1px solid #1f232926;
|
||||
|
||||
.list-content {
|
||||
height: calc(100% - 56px);
|
||||
padding: 0 16px;
|
||||
|
||||
.model {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 8px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
.name {
|
||||
margin-left: 8px;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
}
|
||||
&:hover {
|
||||
background: #1f23291a;
|
||||
}
|
||||
|
||||
&.isActive {
|
||||
background: #1cba901a;
|
||||
color: var(--ed-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,66 @@
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, ref } from 'vue'
|
||||
import DatasourceForm from './DatasourceForm.vue'
|
||||
import icon_close_outlined from '@/assets/svg/operate/ope-close.svg'
|
||||
|
||||
const datasourceFormRef = ref()
|
||||
const datasourceConfigvVisible = ref(false)
|
||||
const beforeClose = () => {
|
||||
datasourceConfigvVisible.value = false
|
||||
}
|
||||
|
||||
const emit = defineEmits(['refresh'])
|
||||
const refresh = () => {
|
||||
emit('refresh')
|
||||
}
|
||||
const changeActiveStep = (val: any) => {
|
||||
if (val === 0) {
|
||||
datasourceConfigvVisible.value = false
|
||||
}
|
||||
}
|
||||
const save = () => {
|
||||
datasourceFormRef.value.tableListSave()
|
||||
}
|
||||
|
||||
const open = (item: any) => {
|
||||
datasourceConfigvVisible.value = true
|
||||
nextTick(() => {
|
||||
datasourceFormRef.value.initForm(item, true)
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
open,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-drawer
|
||||
v-model="datasourceConfigvVisible"
|
||||
:close-on-click-modal="false"
|
||||
size="calc(100% - 100px)"
|
||||
modal-class="datasource-drawer-fullscreen"
|
||||
direction="btt"
|
||||
:before-close="beforeClose"
|
||||
:show-close="false"
|
||||
>
|
||||
<template #header="{ close }">
|
||||
<span style="white-space: nowrap">选择数据表</span>
|
||||
<el-icon style="cursor: pointer" @click="close">
|
||||
<icon_close_outlined></icon_close_outlined>
|
||||
</el-icon>
|
||||
</template>
|
||||
<DatasourceForm
|
||||
ref="datasourceFormRef"
|
||||
:active-step="2"
|
||||
@change-active-step="changeActiveStep"
|
||||
@refresh="refresh"
|
||||
></DatasourceForm>
|
||||
<template #footer>
|
||||
<el-button secondary @click="beforeClose"> 取消 </el-button>
|
||||
<el-button type="primary" @click="save"> 保存 </el-button>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
@@ -105,7 +105,7 @@
|
||||
@closed="closeTable"
|
||||
>
|
||||
<div>{{ t('ds.edit.table_comment_label') }}</div>
|
||||
<el-input v-model="tableComment" />
|
||||
<el-input v-model="tableComment" :rows="3" type="textarea" />
|
||||
<div style="display: flex; justify-content: flex-end; margin-top: 20px">
|
||||
<el-button @click="closeTable">{{ t('common.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="saveTable">{{ t('common.confirm') }}</el-button>
|
||||
@@ -121,7 +121,7 @@
|
||||
@closed="closeField"
|
||||
>
|
||||
<div>{{ t('ds.edit.field_comment_label') }}</div>
|
||||
<el-input v-model="fieldComment" />
|
||||
<el-input v-model="fieldComment" :rows="3" type="textarea" />
|
||||
<div style="display: flex; justify-content: flex-end; margin-top: 20px">
|
||||
<el-button @click="closeField">{{ t('common.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="saveField">{{ t('common.confirm') }}</el-button>
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
import mysql_ds from '@/assets/datasource/icon_mysql.png'
|
||||
import excel from '@/assets/datasource/icon_excel.png'
|
||||
import oracle from '@/assets/datasource/icon_oracle.png'
|
||||
import pg from '@/assets/datasource/icon_PostgreSQL.png'
|
||||
import sqlServer from '@/assets/datasource/icon_SQL_Server.png'
|
||||
|
||||
export const dsType = [
|
||||
{ label: 'MySQL', value: 'mysql' },
|
||||
{ label: 'PostgreSQL', value: 'pg' },
|
||||
@@ -6,4 +12,12 @@ export const dsType = [
|
||||
{ label: 'Excel/CSV', value: 'excel' },
|
||||
]
|
||||
|
||||
export const dsTypeWithImg = [
|
||||
{ name: 'MySQL', type: 'mysql', img: mysql_ds },
|
||||
{ name: 'PostgreSQL', type: 'pg', img: pg },
|
||||
{ name: 'Microsoft SQL Server', type: 'sqlServer', img: sqlServer },
|
||||
{ name: 'Oracle', type: 'oracle', img: oracle },
|
||||
{ name: '本地 Excel/CSV', type: 'excel', img: excel },
|
||||
]
|
||||
|
||||
export const haveSchema = ['sqlServer', 'pg', 'oracle']
|
||||
|
||||
@@ -48,14 +48,14 @@ const handleDel = () => {
|
||||
</div>
|
||||
<div class="methods">
|
||||
<el-tooltip :offset="14" effect="dark" content="编辑" placement="top">
|
||||
<el-icon @click="handleEdit" size="16">
|
||||
<el-icon size="16" @click="handleEdit">
|
||||
<edit></edit>
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
<template v-if="!isDefault">
|
||||
<span class="divide"></span>
|
||||
<el-tooltip :offset="14" effect="dark" content="删除" placement="top">
|
||||
<el-icon @click="handleDel" size="16">
|
||||
<el-icon size="16" @click="handleDel">
|
||||
<delIcon></delIcon>
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
|
||||
@@ -193,8 +193,8 @@ const submit = (item: any) => {
|
||||
<span class="title">AI 模型配置</span>
|
||||
<div class="button-input">
|
||||
<el-input
|
||||
clearable
|
||||
v-model="keywords"
|
||||
clearable
|
||||
style="width: 240px; margin-right: 12px"
|
||||
placeholder="搜索"
|
||||
>
|
||||
@@ -216,8 +216,8 @@ const submit = (item: any) => {
|
||||
>
|
||||
<div class="popover">
|
||||
<el-input
|
||||
clearable
|
||||
v-model="defaultModelKeywords"
|
||||
clearable
|
||||
style="width: 100%; margin-right: 12px"
|
||||
placeholder="通过名称搜索"
|
||||
>
|
||||
@@ -229,10 +229,10 @@ const submit = (item: any) => {
|
||||
</el-input>
|
||||
<div class="popover-content">
|
||||
<div
|
||||
class="popover-item"
|
||||
:class="currentDefaultModel === ele.name && 'isActive'"
|
||||
v-for="ele in defaultModelListWithSearch"
|
||||
:key="ele.name"
|
||||
class="popover-item"
|
||||
:class="currentDefaultModel === ele.name && 'isActive'"
|
||||
@click="handleDefaultModelChange(ele)"
|
||||
>
|
||||
<img :src="ele.img" width="24px" height="24px" />
|
||||
@@ -265,22 +265,23 @@ const submit = (item: any) => {
|
||||
<div v-else class="card-content">
|
||||
<Card
|
||||
v-for="ele in modelListWithSearch"
|
||||
:name="ele.name"
|
||||
:modleType="getModelTypeName(ele.type)"
|
||||
:baseModle="ele.baseModle"
|
||||
:id="ele.id"
|
||||
:key="ele.id"
|
||||
:name="ele.name"
|
||||
:modle-type="getModelTypeName(ele.type)"
|
||||
:base-modle="ele.baseModle"
|
||||
@edit="handleEditModel"
|
||||
@del="deleteHandler"
|
||||
></Card>
|
||||
</div>
|
||||
<el-drawer
|
||||
v-model="modelConfigvVisible"
|
||||
:close-on-click-modal="false"
|
||||
size="calc(100% - 100px)"
|
||||
modal-class="model-drawer-fullscreen"
|
||||
direction="btt"
|
||||
:before-close="beforeClose"
|
||||
:show-close="false"
|
||||
v-model="modelConfigvVisible"
|
||||
>
|
||||
<template #header="{ close }">
|
||||
<span style="white-space: nowrap">添加模型</span>
|
||||
@@ -294,26 +295,26 @@ const submit = (item: any) => {
|
||||
</el-step>
|
||||
</el-steps>
|
||||
</div>
|
||||
<el-icon @click="close" style="cursor: pointer">
|
||||
<el-icon style="cursor: pointer" @click="close">
|
||||
<icon_close_outlined></icon_close_outlined>
|
||||
</el-icon>
|
||||
</template>
|
||||
<ModelList v-if="activeStep === 0" @clickModel="clickModel"></ModelList>
|
||||
<ModelList v-if="activeStep === 0" @click-model="clickModel"></ModelList>
|
||||
<ModelListSide
|
||||
:activeName="activeName"
|
||||
v-if="activeStep === 1"
|
||||
@clickModel="clickModelSide"
|
||||
:active-name="activeName"
|
||||
@click-model="clickModelSide"
|
||||
></ModelListSide>
|
||||
<ModelForm
|
||||
@submit="submit"
|
||||
ref="modelFormRef"
|
||||
:activeName="activeName"
|
||||
v-if="activeStep === 1"
|
||||
ref="modelFormRef"
|
||||
:active-name="activeName"
|
||||
@submit="submit"
|
||||
></ModelForm>
|
||||
<template #footer>
|
||||
<el-button @click="cancel" secondary> 取消 </el-button>
|
||||
<el-button @click="preStep" secondary> 上一步 </el-button>
|
||||
<el-button @click="saveModel" type="primary"> 保存 </el-button>
|
||||
<el-button secondary @click="cancel"> 取消 </el-button>
|
||||
<el-button secondary @click="preStep"> 上一步 </el-button>
|
||||
<el-button type="primary" @click="saveModel"> 保存 </el-button>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</div>
|
||||
|
||||
@@ -22,7 +22,7 @@ interface Options {
|
||||
value: string
|
||||
}
|
||||
|
||||
interface ParamsForm {
|
||||
interface Form {
|
||||
name: string
|
||||
id: string
|
||||
}
|
||||
@@ -41,7 +41,7 @@ const modelForm = reactive({
|
||||
let isCreate = false
|
||||
const modelRef = ref()
|
||||
const paramsFormRef = ref()
|
||||
const advancedSetting = ref([] as ParamsForm[])
|
||||
const advancedSetting = ref([] as Form[])
|
||||
const modelList = ref([] as Options[])
|
||||
const paramsFormDrawer = ref(false)
|
||||
const advancedSettingExpand = ref(false)
|
||||
@@ -185,13 +185,13 @@ defineExpose({
|
||||
<el-input v-model="modelForm.endpoint" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="api_key" label="API Key">
|
||||
<el-input type="password" show-password v-model="modelForm.api_key" />
|
||||
<el-input v-model="modelForm.api_key" type="password" show-password />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div
|
||||
@click="advancedSettingExpand = !advancedSettingExpand"
|
||||
class="advance-setting"
|
||||
:class="advancedSettingExpand && 'expand'"
|
||||
@click="advancedSettingExpand = !advancedSettingExpand"
|
||||
>
|
||||
高级设置
|
||||
<el-icon size="16">
|
||||
@@ -200,7 +200,7 @@ defineExpose({
|
||||
</div>
|
||||
<div v-if="advancedSettingExpand" class="model-params">
|
||||
模型参数
|
||||
<span @click="handleParamsCreate" class="add">
|
||||
<span class="add" @click="handleParamsCreate">
|
||||
<el-icon size="16">
|
||||
<icon_add_outlined></icon_add_outlined>
|
||||
</el-icon>
|
||||
@@ -215,12 +215,12 @@ defineExpose({
|
||||
<el-table-column prop="value" label="参数值" />
|
||||
<el-table-column fixed="right" width="80" class-name="operation-column_text" label="操作">
|
||||
<template #default="scope">
|
||||
<el-button text @click="handleParamsEdite(scope.row)" type="primary">
|
||||
<el-button text type="primary" @click="handleParamsEdite(scope.row)">
|
||||
<el-icon size="16">
|
||||
<icon_edit_outlined></icon_edit_outlined>
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<el-button text @click="handleParamsDel(scope.row)" type="primary">
|
||||
<el-button text type="primary" @click="handleParamsDel(scope.row)">
|
||||
<el-icon size="16">
|
||||
<icon_delete></icon_delete>
|
||||
</el-icon>
|
||||
@@ -230,11 +230,11 @@ defineExpose({
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
<el-drawer :size="600" v-model="paramsFormDrawer" title="添加参数">
|
||||
<ParamsForm @submit="submit" ref="paramsFormRef"></ParamsForm>
|
||||
<el-drawer v-model="paramsFormDrawer" :size="600" title="添加参数">
|
||||
<ParamsForm ref="paramsFormRef" @submit="submit"></ParamsForm>
|
||||
<template #footer>
|
||||
<el-button @click="cancel" secondary> 取消 </el-button>
|
||||
<el-button @click="addParams" type="primary"> 添加 </el-button>
|
||||
<el-button secondary @click="cancel"> 取消 </el-button>
|
||||
<el-button type="primary" @click="addParams"> 添加 </el-button>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</div>
|
||||
|
||||
@@ -36,8 +36,8 @@ const handleModelClick = (item: any) => {
|
||||
<div class="model-list">
|
||||
<div class="title">选择供应商</div>
|
||||
<el-input
|
||||
clearable
|
||||
v-model="keywords"
|
||||
clearable
|
||||
style="width: 100%; margin-right: 12px"
|
||||
placeholder="搜索"
|
||||
>
|
||||
@@ -49,10 +49,10 @@ const handleModelClick = (item: any) => {
|
||||
</el-input>
|
||||
<div class="list-content">
|
||||
<div
|
||||
@click="handleModelClick(ele)"
|
||||
class="model"
|
||||
v-for="ele in modelListWithSearch"
|
||||
:key="ele.name"
|
||||
class="model"
|
||||
@click="handleModelClick(ele)"
|
||||
>
|
||||
<img width="32px" height="32px" :src="ele.img" />
|
||||
<span class="name">{{ ele.name }}</span>
|
||||
@@ -61,7 +61,7 @@ const handleModelClick = (item: any) => {
|
||||
v-if="!!keywords && !modelListWithSearch.length"
|
||||
:description="'没有找到相关内容'"
|
||||
img-type="tree"
|
||||
style="width: 100%;margin-top: 100px;"
|
||||
style="width: 100%; margin-top: 100px"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -71,7 +71,7 @@ const handleModelClick = (item: any) => {
|
||||
.model-list {
|
||||
width: 800px;
|
||||
margin: 0 auto;
|
||||
height: 100%;
|
||||
max-height: 100%;
|
||||
padding-top: 24px;
|
||||
.title {
|
||||
font-weight: 500;
|
||||
@@ -86,6 +86,10 @@ const handleModelClick = (item: any) => {
|
||||
justify-content: space-between;
|
||||
height: calc(100% - 40px);
|
||||
|
||||
.model:nth-child(odd) {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.model {
|
||||
width: 392px;
|
||||
height: 64px;
|
||||
@@ -93,6 +97,7 @@ const handleModelClick = (item: any) => {
|
||||
align-items: center;
|
||||
padding-left: 16px;
|
||||
margin-bottom: 16px;
|
||||
margin-left: 16px;
|
||||
flex-wrap: wrap;
|
||||
border: 1px solid #dee0e3;
|
||||
border-radius: 4px;
|
||||
|
||||
@@ -43,8 +43,8 @@ const handleModelClick = (item: any) => {
|
||||
<template>
|
||||
<div class="model-list_side">
|
||||
<el-input
|
||||
clearable
|
||||
v-model="keywords"
|
||||
clearable
|
||||
style="width: 232px; margin: 16px 0 8px 24px"
|
||||
placeholder="搜索"
|
||||
>
|
||||
@@ -56,11 +56,11 @@ const handleModelClick = (item: any) => {
|
||||
</el-input>
|
||||
<div class="list-content">
|
||||
<div
|
||||
@click="handleModelClick(ele)"
|
||||
class="model"
|
||||
:class="activeName === ele.name && 'isActive'"
|
||||
v-for="ele in modelListWithSearch"
|
||||
:key="ele.name"
|
||||
class="model"
|
||||
:class="activeName === ele.name && 'isActive'"
|
||||
@click="handleModelClick(ele)"
|
||||
>
|
||||
<img width="32px" height="32px" :src="ele.img" />
|
||||
<span class="name">{{ ele.name }}</span>
|
||||
|
||||
@@ -66,11 +66,11 @@ defineExpose({
|
||||
controls-position="right"
|
||||
/>
|
||||
<el-input
|
||||
placeholder="请输入 1-50 个字符"
|
||||
v-if="paramsForm.type === 0"
|
||||
v-model="paramsForm.value"
|
||||
placeholder="请输入 1-50 个字符"
|
||||
/>
|
||||
<el-input type="textarea" v-if="paramsForm.type === 2" v-model="paramsForm.value" />
|
||||
<el-input v-if="paramsForm.type === 2" v-model="paramsForm.value" type="textarea" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||