Merge branch 'dev'
@@ -37,6 +37,7 @@
|
||||
"vue-dompurify-html": "^5.3.0",
|
||||
"vue-i18n": "^9.14.4",
|
||||
"vue-router": "^4.5.0",
|
||||
"vue-types": "^6.0.0",
|
||||
"web-storage-cache": "^1.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { request } from '@/utils/request'
|
||||
|
||||
export const userImportApi = {
|
||||
downExcelTemplateApi: () => request.post('/user/excelTemplate', {}, { responseType: 'blob' }),
|
||||
importUserApi: (data: any) =>
|
||||
request.post('/user/batchImport', data, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
}),
|
||||
downErrorRecordApi: (key: string) =>
|
||||
request.get(`/user/errorRecord/${key}`, { responseType: 'blob' }),
|
||||
clearErrorApi: (key: string) => {
|
||||
request.get(`/user/clearErrorRecord/${key}`)
|
||||
},
|
||||
}
|
||||
|
||||
export const userApi = {
|
||||
pager: (params: any, pageNumber: number, pageSize: number) =>
|
||||
request.get(`/user/pager/${pageNumber}/${pageSize}${params}`),
|
||||
add: (data: any) => request.post('/user', data),
|
||||
edit: (data: any) => request.put('/user', data),
|
||||
clearErrorApi: (key: string) => request.get(`/user/clearErrorRecord/${key}`),
|
||||
delete: (key: string) => request.delete(`/user/${key}`),
|
||||
get: (key: string) => request.get(`/user/${key}`),
|
||||
pwd: (data: any) => request.put('/user/pwd', data),
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { request } from '@/utils/request'
|
||||
|
||||
export const workspaceUserList = (params: any, pageNum: number, pageSize: number) =>
|
||||
request.get(`/system/workspace/uws/pager/${pageNum}/${pageSize}`, { params })
|
||||
|
||||
export const workspaceCreate = (data: any) => request.post('/system/workspace/uws', data)
|
||||
export const workspaceDelete = (data: any) => request.delete('/system/workspace/uws', data)
|
||||
export const workspaceList = () => request.get('/system/workspace')
|
||||
export const workspaceDetail = (id: any) => request.get(`/system/workspace/${id}`)
|
||||
@@ -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_1617_1677)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.666748 7.99996C0.666748 12.0505 3.94956 15.3333 8.00008 15.3333C12.0506 15.3333 15.3334 12.0505 15.3334 7.99996C15.3334 3.94944 12.0506 0.666626 8.00008 0.666626C3.94956 0.666626 0.666748 3.94944 0.666748 7.99996Z" fill="#34C724"/>
|
||||
<path d="M10.7621 5.98431L7.22639 9.51998L5.49305 7.78665C5.43054 7.72416 5.34578 7.68905 5.25739 7.68905C5.169 7.68905 5.08423 7.72416 5.02172 7.78665L4.55072 8.25831C4.48823 8.32082 4.45312 8.40559 4.45312 8.49398C4.45312 8.58237 4.48823 8.66714 4.55072 8.72964L6.99072 11.17C7.05323 11.2325 7.138 11.2676 7.22639 11.2676C7.31477 11.2676 7.39954 11.2325 7.46205 11.17L11.7047 6.92731C11.7672 6.8648 11.8023 6.78003 11.8023 6.69165C11.8023 6.60326 11.7672 6.51849 11.7047 6.45598L11.2334 5.98431C11.1709 5.92182 11.0861 5.88672 10.9977 5.88672C10.9093 5.88672 10.8246 5.92182 10.7621 5.98431Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1617_1677">
|
||||
<rect width="16" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.33317 6.66807V12.5416C9.33317 12.7027 9.20259 12.8333 9.0415 12.8333H8.45817C8.29709 12.8333 8.1665 12.7027 8.1665 12.5416V6.41663C8.1665 6.3309 8.185 6.24949 8.21821 6.17618C8.26253 6.06615 8.34187 5.96924 8.45144 5.90383L11.6665 3.98466V2.33329H2.33317V4.00501L5.45111 5.86895C5.6741 5.95092 5.83317 6.1652 5.83317 6.41663V10.5C5.83317 10.661 5.70259 10.7916 5.5415 10.7916H4.95817C4.79709 10.7916 4.6665 10.661 4.6665 10.5V6.68798L1.4429 4.76088C1.37054 4.71762 1.31141 4.66061 1.26688 4.59514C1.20538 4.54167 1.1665 4.46286 1.1665 4.37496V1.74996C1.1665 1.42779 1.42767 1.16663 1.74984 1.16663H12.2498C12.4109 1.16663 12.5568 1.23192 12.6623 1.33748C12.7679 1.44304 12.8332 1.58888 12.8332 1.74996V4.19593C12.8377 4.2369 12.8376 4.27801 12.8332 4.31851V4.37496C12.8332 4.4732 12.7846 4.5601 12.7102 4.61294C12.6698 4.66245 12.6203 4.70587 12.5624 4.74046L9.33317 6.66807Z" fill="#1F2329"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -4,12 +4,12 @@
|
||||
<path d="M11.5556 11.1111C11.5556 13.5657 13.5454 15.5556 16 15.5556C18.4546 15.5556 20.4444 13.5657 20.4444 11.1111C20.4444 8.65653 18.4546 6.66669 16 6.66669C13.5454 6.66669 11.5556 8.65653 11.5556 11.1111Z" fill="url(#paint1_linear_477_6741)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_477_6741" x1="16" y1="6.66669" x2="16" y2="24.8889" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="white" stop-opacity="0"/>
|
||||
<stop stop-color="#ffffff"/>
|
||||
<stop offset="1" stop-color="#ffffff" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_477_6741" x1="16" y1="6.66669" x2="16" y2="24.8889" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="white" stop-opacity="0"/>
|
||||
<stop stop-color="#ffffff"/>
|
||||
<stop offset="1" stop-color="#ffffff" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.3334 4.33333C10.3334 2.49233 8.84004 1 7.00004 1C5.16004 1 3.66671 2.49233 3.66671 4.33333C3.66671 6.17433 5.16004 7.66667 7.00004 7.66667C8.84004 7.66667 10.3334 6.17433 10.3334 4.33333ZM7.00004 6.33333C5.89671 6.33333 5.00004 5.438 5.00004 4.33333C5.00004 3.22867 5.89671 2.33333 7.00004 2.33333C8.10337 2.33333 9.00004 3.22867 9.00004 4.33333C9.00004 5.438 8.10337 6.33333 7.00004 6.33333Z" fill="#646A73"/>
|
||||
<path d="M2.66671 12.6667C2.66671 12.4363 2.69671 12.213 2.75004 12C3.04671 10.85 4.09004 10 5.33337 10H8.01668C8.20077 10 8.35001 9.85076 8.35001 9.66667V9C8.35001 8.81591 8.20077 8.66667 8.01668 8.66667H5.33337C3.12337 8.66667 1.33337 10.4577 1.33337 12.6667V14C1.33337 14.3683 1.63337 14.6667 2.00004 14.6667H8.01668C8.20077 14.6667 8.35001 14.5174 8.35001 14.3333V13.6667C8.35001 13.4826 8.20077 13.3333 8.01668 13.3333H2.66671V12.6667Z" fill="#646A73"/>
|
||||
<path d="M12.1312 10.3738L13.11 11.3526H9.66671C9.48261 11.3526 9.33337 11.5019 9.33337 11.686V12.3526C9.33337 12.5367 9.48261 12.686 9.66671 12.686H13.1188L12.1312 13.6736C12.001 13.8038 12.001 14.0148 12.1312 14.145L12.6026 14.6164C12.7328 14.7466 12.9438 14.7466 13.074 14.6164L15.431 12.2594C15.5612 12.1292 15.5612 11.9182 15.431 11.788L13.074 9.43096C12.9438 9.30079 12.7328 9.30079 12.6026 9.43096L12.1312 9.90237C12.001 10.0325 12.001 10.2436 12.1312 10.3738Z" fill="#646A73"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.99992 0.333252C3.76574 0.333252 0.333252 3.76574 0.333252 7.99992C0.333252 12.2341 3.76574 15.6666 7.99992 15.6666C12.2341 15.6666 15.6666 12.2341 15.6666 7.99992C15.6666 3.76574 12.2341 0.333252 7.99992 0.333252ZM12.6514 11.2374L4.76246 3.34847C5.68019 2.70844 6.79587 2.33325 7.99992 2.33325C11.1295 2.33325 13.6666 4.87031 13.6666 7.99992C13.6666 9.20397 13.2914 10.3196 12.6514 11.2374ZM2.33325 7.99992C2.33325 6.79599 2.70837 5.68041 3.34829 4.76272L11.2371 12.6516C10.3194 13.2915 9.20385 13.6666 7.99992 13.6666C4.87031 13.6666 2.33325 11.1295 2.33325 7.99992Z" fill="#8F959E"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 701 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="M3.11117 3.11117V12.8889H5.33339V14.2223H2.66672C2.21895 14.2223 1.77783 13.8036 1.77783 13.3334V2.66672C1.77783 2.19651 2.21895 1.77783 2.66672 1.77783H13.3334C13.7812 1.77783 14.2223 2.19651 14.2223 2.66672V13.3334C14.2223 13.8036 13.7812 14.2223 13.3334 14.2223H10.6667V12.8889H12.8889V3.11117H3.11117Z" fill="#1F2329"/>
|
||||
<path d="M7.33339 13.7778C7.33339 14.0233 7.53237 14.2223 7.77783 14.2223H8.22228C8.46774 14.2223 8.66672 14.0233 8.66672 13.7778V7.14731L10.5237 9.00427C10.6973 9.17784 10.9787 9.17784 11.1522 9.00427L11.4665 8.69001C11.6401 8.51644 11.6401 8.23503 11.4665 8.06147L8.3238 4.91877C8.28396 4.87893 8.23844 4.84823 8.18984 4.82668C8.02382 4.74494 7.81738 4.77314 7.67924 4.91128L4.53655 8.05397C4.36298 8.22754 4.36298 8.50895 4.53655 8.68251L4.85082 8.99678C5.02438 9.17035 5.30579 9.17035 5.47936 8.99678L7.33339 7.14275V13.7778Z" fill="#1F2329"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 984 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.99993 5.29289L9.85868 1.43413C9.95632 1.3365 10.1146 1.3365 10.2122 1.43413L10.5658 1.78769C10.6634 1.88532 10.6634 2.04361 10.5658 2.14124L6.70703 6L10.5658 9.85875C10.6634 9.95639 10.6634 10.1147 10.5658 10.2123L10.2122 10.5659C10.1146 10.6635 9.95632 10.6635 9.85868 10.5659L5.99993 6.7071L2.14117 10.5659C2.04354 10.6635 1.88525 10.6635 1.78762 10.5659L1.43406 10.2123C1.33643 10.1147 1.33643 9.95639 1.43406 9.85875L5.29282 6L1.43406 2.14124C1.33643 2.04361 1.33643 1.88532 1.43406 1.78769L1.78762 1.43413C1.88525 1.3365 2.04354 1.3365 2.14117 1.43413L5.99993 5.29289Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 680 B |
@@ -0,0 +1,12 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1515_53088)">
|
||||
<path d="M7.99984 15.3334C12.0499 15.3334 15.3332 12.0502 15.3332 8.00008C15.3332 3.94999 12.0499 0.666748 7.99984 0.666748C3.94975 0.666748 0.666504 3.94999 0.666504 8.00008C0.666504 12.0502 3.94975 15.3334 7.99984 15.3334Z" fill="#1CBA90"/>
|
||||
<path d="M7.53927 5.64121C7.57971 5.65796 7.62306 5.66659 7.66683 5.66659H8.3335C8.37727 5.66659 8.42062 5.65796 8.46106 5.64121C8.5015 5.62446 8.53825 5.59991 8.5692 5.56895C8.60015 5.538 8.62471 5.50126 8.64146 5.46081C8.65821 5.42037 8.66683 5.37703 8.66683 5.33325V4.66658C8.66683 4.62281 8.65821 4.57947 8.64146 4.53902C8.62471 4.49858 8.60015 4.46184 8.5692 4.43088C8.53825 4.39993 8.5015 4.37538 8.46106 4.35863C8.42062 4.34187 8.37727 4.33325 8.3335 4.33325H7.66683C7.62306 4.33325 7.57971 4.34187 7.53927 4.35863C7.49883 4.37538 7.46208 4.39993 7.43113 4.43088C7.40017 4.46184 7.37562 4.49858 7.35887 4.53902C7.34212 4.57947 7.3335 4.62281 7.3335 4.66658V5.33325C7.3335 5.37703 7.34212 5.42037 7.35887 5.46081C7.37562 5.50126 7.40017 5.538 7.43113 5.56895C7.46208 5.59991 7.49883 5.62446 7.53927 5.64121Z" fill="white"/>
|
||||
<path d="M7.3335 7.66659V10.3333H6.66683C6.62306 10.3333 6.57971 10.3419 6.53927 10.3586C6.49883 10.3754 6.46208 10.3999 6.43113 10.4309C6.40017 10.4618 6.37562 10.4986 6.35887 10.539C6.34212 10.5795 6.3335 10.6228 6.3335 10.6666V11.3333C6.3335 11.377 6.34212 11.4204 6.35887 11.4608C6.37562 11.5013 6.40017 11.538 6.43113 11.569C6.46208 11.5999 6.49883 11.6245 6.53927 11.6412C6.57971 11.658 6.62306 11.6666 6.66683 11.6666H9.3335C9.37727 11.6666 9.42062 11.658 9.46106 11.6412C9.5015 11.6245 9.53825 11.5999 9.5692 11.569C9.60015 11.538 9.62471 11.5013 9.64146 11.4608C9.65821 11.4204 9.66683 11.377 9.66683 11.3333V10.6666C9.66683 10.6228 9.65821 10.5795 9.64146 10.539C9.62471 10.4986 9.60015 10.4618 9.5692 10.4309C9.53825 10.3999 9.5015 10.3754 9.46106 10.3586C9.42062 10.3419 9.37727 10.3333 9.3335 10.3333H8.66683V6.66659C8.66696 6.62277 8.65843 6.57937 8.64173 6.53887C8.62502 6.49837 8.60047 6.46157 8.56949 6.43059C8.53851 6.39961 8.50172 6.37506 8.46121 6.35836C8.42071 6.34165 8.37731 6.33312 8.3335 6.33325H7.00016C6.95639 6.33325 6.91304 6.34187 6.8726 6.35863C6.83216 6.37538 6.79541 6.39993 6.76446 6.43088C6.73351 6.46184 6.70895 6.49858 6.6922 6.53902C6.67545 6.57947 6.66683 6.62281 6.66683 6.66659V7.33325C6.66683 7.37703 6.67545 7.42037 6.6922 7.46081C6.70895 7.50126 6.73351 7.538 6.76446 7.56895C6.79541 7.59991 6.83216 7.62446 6.8726 7.64121C6.91304 7.65796 6.95639 7.66659 7.00016 7.66659H7.3335Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1515_53088">
|
||||
<rect width="16" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
@@ -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="M10 8.66667H6C4.15905 8.66667 2 9.95432 2 12.2667V14.0667C2 14.398 2.29848 14.6667 2.66667 14.6667H13.3333C13.7015 14.6667 14 14.398 14 14.0667V12.2667C14 9.95237 11.841 8.66667 10 8.66667Z" fill="#8F959E"/>
|
||||
<path d="M4.66667 4.66667C4.66667 6.50762 6.15905 8 8 8C9.84095 8 11.3333 6.50762 11.3333 4.66667C11.3333 2.82572 9.84095 1.33333 8 1.33333C6.15905 1.33333 4.66667 2.82572 4.66667 4.66667Z" fill="#8F959E"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 527 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.25 9.75H6.75C4.67893 9.75 2.25 11.1986 2.25 13.8V15.825C2.25 16.1978 2.58579 16.5 3 16.5H15C15.4142 16.5 15.75 16.1978 15.75 15.825V13.8C15.75 11.1964 13.3211 9.75 11.25 9.75Z" fill="#1CBA90"/>
|
||||
<path d="M5.25 5.25C5.25 7.32107 6.92893 9 9 9C11.0711 9 12.75 7.32107 12.75 5.25C12.75 3.17893 11.0711 1.5 9 1.5C6.92893 1.5 5.25 3.17893 5.25 5.25Z" fill="#1CBA90"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 478 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="M5.35399 11.2488H7.62467L14.3787 4.4946C14.6108 4.26253 14.6113 3.88643 14.3798 3.65375L12.3342 1.59735C12.1017 1.36485 11.7247 1.36485 11.4922 1.59735L11.0723 2.01722L11.074 2.01887L4.75864 8.3828V10.6535C4.75864 10.9823 5.02519 11.2488 5.35399 11.2488ZM11.8943 2.84351L13.1 4.05552L12.0797 5.07678L10.8803 3.87736L11.8943 2.84351ZM11.2382 5.91913L7.1225 10.0388H7.12068L5.9687 8.88498L10.0465 4.72746L11.2382 5.91913Z" fill="#646A73"/>
|
||||
<path d="M8.76313 2.316H2.44339C2.07805 2.316 1.78189 2.61216 1.78189 2.9775V13.5615C1.78189 13.9268 2.07805 14.223 2.44339 14.223H13.0274C13.3927 14.223 13.6889 13.9268 13.6889 13.5615V7.1614L12.3576 8.50072V12.8982H3.11313V3.6408H7.46239L8.76313 2.316Z" fill="#646A73"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 823 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 d="M14 12.3333V14C14 14.3682 13.7015 14.6667 13.3333 14.6667H2.66667C2.29848 14.6667 2 14.3682 2 14V12.3333C2 12.1492 2.14924 12 2.33333 12H3C3.18409 12 3.33333 12.1492 3.33333 12.3333V13.3333H12.6667V12.3333C12.6667 12.1492 12.8159 12 13 12H13.6667C13.8508 12 14 12.1492 14 12.3333ZM8.66667 4.33329L10.6736 6.34027C10.8038 6.47045 11.0149 6.47045 11.1451 6.34027L11.6165 5.86887C11.7466 5.73869 11.7466 5.52764 11.6165 5.39746L8.31663 2.09763C8.25154 2.03254 8.16623 2 8.08092 2C7.99562 2 7.91031 2.03254 7.84522 2.09763L4.54539 5.39746C4.41521 5.52764 4.41521 5.73869 4.54539 5.86887L5.01679 6.34027C5.14697 6.47045 5.35802 6.47045 5.4882 6.34027L7.33334 4.49513V11.6904C7.33334 11.8745 7.48257 12.0238 7.66667 12.0238H8.33334C8.51743 12.0238 8.66667 11.8745 8.66667 11.6904V4.33329Z" fill="#2B2F36"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 913 B |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,3 @@
|
||||
import DrawerFilter from './src/DrawerFilter.vue'
|
||||
|
||||
export { DrawerFilter }
|
||||
@@ -0,0 +1,115 @@
|
||||
<script setup lang="ts">
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { reactive } from 'vue'
|
||||
import { useEmitt } from '@/utils/useEmitt'
|
||||
|
||||
const props = defineProps({
|
||||
optionList: propTypes.arrayOf(
|
||||
propTypes.shape({
|
||||
id: propTypes.string,
|
||||
name: propTypes.string,
|
||||
})
|
||||
),
|
||||
index: propTypes.number,
|
||||
title: propTypes.string,
|
||||
})
|
||||
|
||||
const state = reactive<{ activeStatus: any[]; optionList: any[] }>({
|
||||
activeStatus: [],
|
||||
optionList: [],
|
||||
})
|
||||
|
||||
const nodeChange = (id: string | number) => {
|
||||
const len = state.activeStatus.indexOf(id)
|
||||
if (len >= 0) {
|
||||
state.activeStatus.splice(len, 1)
|
||||
} else {
|
||||
state.activeStatus.push(id)
|
||||
}
|
||||
emits('filter-change', state.activeStatus)
|
||||
}
|
||||
|
||||
const emits = defineEmits(['filter-change'])
|
||||
const clear = (index: number) => {
|
||||
if (index !== props.index) return
|
||||
state.activeStatus = []
|
||||
}
|
||||
|
||||
useEmitt({
|
||||
name: 'clear-drawer_main',
|
||||
callback: clear,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="draw-filter_enum">
|
||||
<span>{{ title }}</span>
|
||||
<div class="filter-item">
|
||||
<span
|
||||
v-for="ele in props.optionList"
|
||||
:key="ele.id"
|
||||
class="item"
|
||||
:class="[state.activeStatus.includes(ele.id) ? 'active' : '']"
|
||||
@click="nodeChange(ele.id)"
|
||||
>{{ ele.name }}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="less" scope>
|
||||
.draw-filter_enum {
|
||||
margin-bottom: 4px;
|
||||
|
||||
> :nth-child(1) {
|
||||
color: var(--deTextSecondary, #1f2329);
|
||||
font-family: var(--de-custom_font, 'PingFang');
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.filter-item {
|
||||
margin-top: 8px;
|
||||
.item,
|
||||
.more {
|
||||
font-family: var(--de-custom_font, 'PingFang');
|
||||
white-space: nowrap;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
margin-right: 12px;
|
||||
text-align: center;
|
||||
padding: 1px 6px;
|
||||
background: var(--deTextPrimary5, #f5f6f7);
|
||||
color: var(--deTextPrimary, #1f2329);
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.active,
|
||||
.more:hover {
|
||||
background: var(--primary10, var(--ed-color-primary-1a, #1cba9033));
|
||||
color: var(--primaryselect, #0c296e);
|
||||
}
|
||||
|
||||
.more {
|
||||
white-space: nowrap;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
i {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style lang="less">
|
||||
.filter-popper {
|
||||
padding: 0 !important;
|
||||
background: #fff !important;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,91 @@
|
||||
<script setup lang="ts">
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { ElSelect, ElOption } from 'element-plus-secondary'
|
||||
import { computed, reactive } from 'vue'
|
||||
import { useEmitt } from '@/utils/useEmitt'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
const { t } = useI18n()
|
||||
const props = defineProps({
|
||||
optionList: propTypes.arrayOf(
|
||||
propTypes.shape({
|
||||
id: propTypes.string,
|
||||
name: propTypes.string,
|
||||
})
|
||||
),
|
||||
index: propTypes.number,
|
||||
title: propTypes.string,
|
||||
property: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
})
|
||||
|
||||
const state = reactive({
|
||||
activeStatus: [],
|
||||
})
|
||||
const emits = defineEmits(['filter-change'])
|
||||
|
||||
const selectStatus = (ids: any[]) => {
|
||||
emits(
|
||||
'filter-change',
|
||||
ids.map((item) => item.id || item.value)
|
||||
)
|
||||
}
|
||||
|
||||
const optionListNotSelect = computed(() => {
|
||||
return [...(props.optionList as any[])]
|
||||
})
|
||||
const clear = (index: number) => {
|
||||
if (index !== props.index) return
|
||||
state.activeStatus = []
|
||||
}
|
||||
|
||||
useEmitt({
|
||||
name: 'clear-drawer_main',
|
||||
callback: clear,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="draw-filter_base">
|
||||
<span>{{ title }}</span>
|
||||
<div class="filter-item">
|
||||
<el-select
|
||||
v-model="state.activeStatus"
|
||||
:teleported="false"
|
||||
style="width: 100%"
|
||||
value-key="id"
|
||||
filterable
|
||||
:placeholder="t('datasource.Please_select') + props.property.placeholder"
|
||||
multiple
|
||||
@change="selectStatus"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in optionListNotSelect"
|
||||
:key="item.name"
|
||||
:label="item.name"
|
||||
:value="item"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="less" scope>
|
||||
.draw-filter_base {
|
||||
margin-bottom: 16px;
|
||||
|
||||
> :nth-child(1) {
|
||||
color: var(--deTextSecondary, #1f2329);
|
||||
font-family: var(--de-custom_font, 'PingFang');
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.filter-item {
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,3 @@
|
||||
import DrawerMain from './src/DrawerMain.vue'
|
||||
|
||||
export { DrawerMain }
|
||||
@@ -0,0 +1,163 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, computed } from 'vue'
|
||||
import { ElDrawer, ElButton } from 'element-plus-secondary'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import DrawerFilter from '@/components/drawer-filter/src/DrawerFilter.vue'
|
||||
import DrawerEnumFilter from '@/components/drawer-filter/src/DrawerEnumFilter.vue'
|
||||
import { useEmitt } from '@/utils/useEmitt'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
const { t } = useI18n()
|
||||
const props = defineProps({
|
||||
filterOptions: propTypes.arrayOf(
|
||||
propTypes.shape({
|
||||
type: propTypes.string,
|
||||
field: propTypes.string,
|
||||
option: propTypes.array,
|
||||
title: propTypes.string,
|
||||
property: propTypes.shape({}),
|
||||
})
|
||||
),
|
||||
title: propTypes.string,
|
||||
})
|
||||
|
||||
const componentList = computed(() => {
|
||||
return props.filterOptions as any[]
|
||||
})
|
||||
|
||||
const state = reactive<{ conditions: any[] }>({
|
||||
conditions: [],
|
||||
})
|
||||
const userDrawer = ref(false)
|
||||
|
||||
const init = () => {
|
||||
userDrawer.value = true
|
||||
}
|
||||
|
||||
const clear = (index: any) => {
|
||||
useEmitt().emitter.emit('clear-drawer_main', index)
|
||||
}
|
||||
const cleanrInnerValue = (index: number) => {
|
||||
const field = componentList.value[index]?.field
|
||||
if (!field) {
|
||||
return
|
||||
}
|
||||
clear(index)
|
||||
for (let i = 0; i < state.conditions.length; i++) {
|
||||
if (state.conditions[i].field === field) {
|
||||
state.conditions[i].value = []
|
||||
}
|
||||
}
|
||||
}
|
||||
const clearInnerTag = (index?: number) => {
|
||||
if (isNaN(index as number)) {
|
||||
for (let i = 0; i < componentList.value.length; i++) {
|
||||
clear(i)
|
||||
}
|
||||
return
|
||||
}
|
||||
const condition = state.conditions[index as number]
|
||||
const field = condition?.field
|
||||
for (let i = 0; i < componentList.value.length; i++) {
|
||||
if (componentList.value[i].field === field) {
|
||||
clear(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
const clearFilter = (id?: number) => {
|
||||
clearInnerTag(id)
|
||||
if (isNaN(id as number)) {
|
||||
const len = state.conditions.length
|
||||
state.conditions.splice(0, len)
|
||||
} else {
|
||||
state.conditions.splice(id as number, 1)
|
||||
}
|
||||
trigger()
|
||||
}
|
||||
const filterChange = (value: any, field: any, operator: any) => {
|
||||
let exits = false
|
||||
let len = state.conditions.length
|
||||
while (len--) {
|
||||
const condition = state.conditions[len]
|
||||
if (condition.field === field) {
|
||||
exits = true
|
||||
condition['value'] = value
|
||||
}
|
||||
if (!condition?.value?.length) {
|
||||
state.conditions.splice(len, 1)
|
||||
}
|
||||
}
|
||||
if (!exits && value?.length) {
|
||||
state.conditions.push({ field, value, operator })
|
||||
}
|
||||
treeFilterChange(value, field, operator)
|
||||
}
|
||||
const reset = () => {
|
||||
clearFilter()
|
||||
userDrawer.value = false
|
||||
}
|
||||
const close = () => {
|
||||
userDrawer.value = false
|
||||
}
|
||||
const emits = defineEmits(['trigger-filter', 'tree-filter-change'])
|
||||
const trigger = () => {
|
||||
emits('trigger-filter', state.conditions)
|
||||
}
|
||||
const treeFilterChange = (value: any, field: any, operator: any) => {
|
||||
emits('tree-filter-change', {
|
||||
value,
|
||||
field,
|
||||
operator,
|
||||
})
|
||||
}
|
||||
defineExpose({
|
||||
init,
|
||||
clearFilter,
|
||||
close,
|
||||
cleanrInnerValue,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-drawer
|
||||
v-model="userDrawer"
|
||||
:title="t('user.filter_conditions')"
|
||||
size="600px"
|
||||
modal-class="drawer-main-container"
|
||||
direction="rtl"
|
||||
>
|
||||
<div v-for="(component, index) in componentList" :key="index">
|
||||
<drawer-filter
|
||||
v-if="component.type === 'select'"
|
||||
:option-list="component.option"
|
||||
:title="component.title"
|
||||
:index="index"
|
||||
:property="component.property"
|
||||
@filter-change="(v) => filterChange(v, component.field, 'in')"
|
||||
/>
|
||||
<drawer-enum-filter
|
||||
v-if="component.type === 'enum'"
|
||||
:option-list="component.option"
|
||||
:title="component.title"
|
||||
:index="index"
|
||||
@filter-change="(v) => filterChange(v, component.field, 'in')"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<el-button secondary @click="reset">{{ t('common.reset') }}</el-button>
|
||||
<el-button type="primary" @click="trigger">{{ t('common.search') }}</el-button>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<style lang="less">
|
||||
.drawer-main-container {
|
||||
.ed-drawer__body {
|
||||
padding: 16px 24px 80px !important;
|
||||
}
|
||||
.ed-drawer__footer {
|
||||
padding: 16px 24px;
|
||||
height: 64px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,91 @@
|
||||
import FilterText from './src/FilterText.vue'
|
||||
|
||||
export { FilterText }
|
||||
|
||||
const fieldText = (field: string, options: any[]) => {
|
||||
for (let index = 0; index < options.length; index++) {
|
||||
const element = options[index]
|
||||
if (field === element.field) {
|
||||
return element.title
|
||||
}
|
||||
}
|
||||
}
|
||||
const valueTextFormTree = (val: string, options: any[]) => {
|
||||
let result = null
|
||||
const stack = [...options]
|
||||
while (stack.length) {
|
||||
const item = stack.pop()
|
||||
if (item.value === val) {
|
||||
result = item.label
|
||||
break
|
||||
}
|
||||
if (item.children?.length) {
|
||||
item.children.forEach((kid: any) => stack.push(kid))
|
||||
}
|
||||
}
|
||||
return result || val
|
||||
}
|
||||
const timestampFormatDate = (value: string) => {
|
||||
if (!value) {
|
||||
return '-'
|
||||
}
|
||||
return new Date(value).toLocaleString()
|
||||
}
|
||||
const valueText = (field: string, val: string, options: any[]) => {
|
||||
for (let index = 0; index < options.length; index++) {
|
||||
const element = options[index]
|
||||
if (field === element.field) {
|
||||
let isTree = false
|
||||
const selectOption = element.option
|
||||
for (let i = 0; i < selectOption.length; i++) {
|
||||
const item = selectOption[i] as any
|
||||
if (item.children !== undefined) {
|
||||
isTree = true
|
||||
break
|
||||
}
|
||||
if (item.id === val || item.value === val) {
|
||||
return item.name || item.label
|
||||
}
|
||||
}
|
||||
if (element.type === 'time') {
|
||||
return timestampFormatDate(val)
|
||||
}
|
||||
if (isTree) {
|
||||
return valueTextFormTree(val, selectOption)
|
||||
}
|
||||
}
|
||||
}
|
||||
return val
|
||||
}
|
||||
export const convertFilterText = (conditions: any[], options: any[]) => {
|
||||
console.log('conditions', conditions, options)
|
||||
|
||||
const result: any[] = []
|
||||
conditions.forEach((condition) => {
|
||||
const field = condition.field
|
||||
const ope = condition.operator
|
||||
const value = condition.value
|
||||
const fieldName = fieldText(field, options)
|
||||
const textOpe = ':'
|
||||
let separator = ','
|
||||
if (ope === 'in') {
|
||||
separator = '、'
|
||||
}
|
||||
if (ope === 'between') {
|
||||
separator = '~'
|
||||
}
|
||||
let valText = value
|
||||
|
||||
if (value) {
|
||||
if (Array.isArray(value)) {
|
||||
const valTextArray = value.map((val) => valueText(field, val, options))
|
||||
valText = valTextArray.join(separator)
|
||||
}
|
||||
if (valText?.length) {
|
||||
const obj = fieldName + textOpe + valText
|
||||
result.push(obj)
|
||||
}
|
||||
}
|
||||
})
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
<script lang="ts" setup>
|
||||
import icon_left_outlined from '@/assets/svg/common-back.svg'
|
||||
import icon_close_outlined from '@/assets/svg/icon_close_outlined.svg'
|
||||
import icon_deleteTrash_outlined from '@/assets/svg/icon_delete.svg'
|
||||
import icon_right_outlined from '@/assets/svg/icon_right_outlined.svg'
|
||||
import { nextTick, ref, watch } from 'vue'
|
||||
import { Icon } from '@/components/icon-custom'
|
||||
import { ElButton, ElDivider, ElIcon } from 'element-plus-secondary'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { t } = useI18n()
|
||||
const props = defineProps({
|
||||
filterTexts: {
|
||||
type: Array<string>,
|
||||
default: () => [],
|
||||
},
|
||||
total: propTypes.number.def(0),
|
||||
})
|
||||
|
||||
const emits = defineEmits(['clearFilter'])
|
||||
const container = ref<any>(null)
|
||||
|
||||
const showScroll = ref(false)
|
||||
const scrollPre = () => {
|
||||
container.value!.scrollLeft -= 10
|
||||
if (container.value.scrollLeft <= 0) {
|
||||
container.value.scrollLeft = 0
|
||||
}
|
||||
}
|
||||
const scrollNext = () => {
|
||||
container.value.scrollLeft += 10
|
||||
const width = container.value.scrollWidth - container.value.offsetWidth
|
||||
if (container.value.scrollLeft > width) {
|
||||
container.value.scrollLeft = width
|
||||
}
|
||||
}
|
||||
const clearFilter = (index?: number) => {
|
||||
emits('clearFilter', index)
|
||||
}
|
||||
|
||||
const clearFilterAll = () => {
|
||||
emits('clearFilter', 'empty')
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.filterTexts,
|
||||
() => {
|
||||
nextTick(() => {
|
||||
showScroll.value = container.value?.scrollWidth > container.value?.offsetWidth
|
||||
})
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="filterTexts.length" class="filter-texts">
|
||||
<span class="sum">{{ total }}</span>
|
||||
<span class="title">{{ t('common.result_count') }}</span>
|
||||
<el-divider direction="vertical" />
|
||||
<el-icon v-if="showScroll" class="arrow-left arrow-filter" @click="scrollPre">
|
||||
<Icon name="icon_left_outlined"><icon_left_outlined class="svg-icon" /></Icon>
|
||||
</el-icon>
|
||||
<div ref="container" class="filter-texts-container">
|
||||
<p v-for="(ele, index) in filterTexts" :key="ele" class="text">
|
||||
<el-tooltip effect="dark" :content="ele" placement="top-start">
|
||||
{{ ele }}
|
||||
</el-tooltip>
|
||||
<el-icon @click="clearFilter(index)">
|
||||
<Icon name="icon_close_outlined"><icon_close_outlined class="svg-icon" /></Icon>
|
||||
</el-icon>
|
||||
</p>
|
||||
<el-button
|
||||
v-if="!showScroll"
|
||||
type="text"
|
||||
class="clear-btn clear-btn-inner"
|
||||
@click="clearFilterAll"
|
||||
>
|
||||
<template #icon>
|
||||
<Icon name="icon_delete-trash_outlined"
|
||||
><icon_deleteTrash_outlined class="svg-icon"
|
||||
/></Icon>
|
||||
</template>
|
||||
{{ t('common.clear_filter') }}</el-button
|
||||
>
|
||||
</div>
|
||||
<el-icon v-if="showScroll" class="arrow-right arrow-filter" @click="scrollNext">
|
||||
<Icon name="icon_right_outlined"><icon_right_outlined class="svg-icon" /></Icon>
|
||||
</el-icon>
|
||||
<el-button
|
||||
v-if="showScroll"
|
||||
type="text"
|
||||
class="clear-btn"
|
||||
style="height: 24px; line-height: 24px"
|
||||
@click="clearFilterAll"
|
||||
>
|
||||
<template #icon>
|
||||
<Icon name="icon_delete-trash_outlined"
|
||||
><icon_deleteTrash_outlined class="svg-icon"
|
||||
/></Icon>
|
||||
</template>
|
||||
{{ t('common.clear_filter') }}</el-button
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.filter-texts {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 16px 0;
|
||||
font-weight: 400;
|
||||
|
||||
.sum {
|
||||
color: #1f2329;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #999999;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.text {
|
||||
max-width: 280px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
padding: 1px 22px 1px 6px;
|
||||
display: inline-block;
|
||||
align-items: center;
|
||||
color: #0c296e;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
background: var(--ed-color-primary-1a, rgba(51, 112, 255, 0.1));
|
||||
border-radius: 2px;
|
||||
margin: 0;
|
||||
margin-right: 8px;
|
||||
position: relative;
|
||||
|
||||
i {
|
||||
position: absolute;
|
||||
right: 6px;
|
||||
top: 50%;
|
||||
font-size: 12px;
|
||||
transform: translateY(-50%);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.clear-btn {
|
||||
color: #646a73;
|
||||
padding: 0 4px;
|
||||
border-radius: 6px;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.clear-btn:hover {
|
||||
background: #1f23291a;
|
||||
}
|
||||
|
||||
.clear-btn:active {
|
||||
background: #1f232933;
|
||||
}
|
||||
|
||||
.filter-texts-container::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.arrow-filter {
|
||||
font-size: 16px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
cursor: pointer;
|
||||
color: #646a73;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.arrow-filter:hover {
|
||||
background: rgba(31, 35, 41, 0.1);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.ed-icon-arrow-right.arrow-filter {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.ed-icon-arrow-left.arrow-filter {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.filter-texts-container {
|
||||
flex: 1;
|
||||
overflow-x: auto;
|
||||
white-space: nowrap;
|
||||
height: 24px;
|
||||
|
||||
.clear-btn-inner {
|
||||
margin-top: -16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -9,11 +9,22 @@ import icon_moments_categories_outlined from '@/assets/svg/icon_moments-categori
|
||||
import icon_side_fold_outlined from '@/assets/svg/icon_side-fold_outlined.svg'
|
||||
import icon_side_expand_outlined from '@/assets/svg/icon_side-expand_outlined.svg'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
const collapse = ref(false)
|
||||
import { useEmitt } from '@/utils/useEmitt'
|
||||
|
||||
const handleCollapseChange = (val: any = true) => {
|
||||
collapse.value = val
|
||||
}
|
||||
useEmitt({
|
||||
name: 'collapse-change',
|
||||
callback: handleCollapseChange,
|
||||
})
|
||||
const handleFoldExpand = () => {
|
||||
collapse.value = !collapse.value
|
||||
}
|
||||
|
||||
const toWorkspace = () => {
|
||||
router.push('/')
|
||||
}
|
||||
@@ -31,11 +42,16 @@ const showSysmenu = computed(() => {
|
||||
<Workspace v-if="!showSysmenu" :collapse="collapse"></Workspace>
|
||||
<Menu :collapse="collapse"></Menu>
|
||||
<div class="bottom">
|
||||
<div v-if="showSysmenu" class="back-to_workspace" @click="toWorkspace">
|
||||
<div
|
||||
v-if="showSysmenu"
|
||||
class="back-to_workspace"
|
||||
:class="collapse && 'collapse'"
|
||||
@click="toWorkspace"
|
||||
>
|
||||
<el-icon size="16">
|
||||
<icon_moments_categories_outlined></icon_moments_categories_outlined>
|
||||
</el-icon>
|
||||
{{ collapse ? '' : '返回工作空间' }}
|
||||
{{ collapse ? '' : $t('workspace.return_to_workspace') }}
|
||||
</div>
|
||||
<div class="personal-info">
|
||||
<Person :collapse="collapse" :in-sysmenu="showSysmenu"></Person>
|
||||
@@ -85,14 +101,17 @@ const showSysmenu = computed(() => {
|
||||
line-height: 22px;
|
||||
width: calc(100% - 32px);
|
||||
.back-to_workspace {
|
||||
background-color: #1f23290a;
|
||||
border: 1px solid #d0d3d6;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 6px;
|
||||
height: 40px;
|
||||
cursor: pointer;
|
||||
|
||||
&:not(.collapse) {
|
||||
background: #1f23290a;
|
||||
border: 1px solid #d9dcdf;
|
||||
}
|
||||
&:hover {
|
||||
background-color: #1f23291a;
|
||||
}
|
||||
@@ -152,6 +171,7 @@ const showSysmenu = computed(() => {
|
||||
flex: 1;
|
||||
padding: 8px 8px 8px 0;
|
||||
max-height: 100vh;
|
||||
overflow-x: auto;
|
||||
|
||||
.content {
|
||||
width: 100%;
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
<script lang="ts">
|
||||
import { h, defineComponent } from 'vue'
|
||||
import { ElMenuItem, ElSubMenu, ElIcon } from 'element-plus-secondary'
|
||||
|
||||
import { useRouter } from 'vue-router'
|
||||
import user from '@/assets/svg/icon_member_filled.svg'
|
||||
import workspace from '@/assets/svg/icon_moments-categories_outlined.svg'
|
||||
import model from '@/assets/svg/icon_dataset_filled.svg'
|
||||
import ds from '@/assets/svg/ds.svg'
|
||||
import dashboard from '@/assets/svg/dashboard.svg'
|
||||
import chat from '@/assets/svg/chat.svg'
|
||||
import icon_user from '@/assets/svg/icon_user.svg'
|
||||
import { useRouter } from 'vue-router'
|
||||
const iconMap = {
|
||||
icon_ai: model,
|
||||
ds,
|
||||
dashboard,
|
||||
chat,
|
||||
user,
|
||||
icon_user: icon_user,
|
||||
workspace,
|
||||
} as { [key: string]: any }
|
||||
|
||||
const titleWithIcon = (props: any) => {
|
||||
@@ -73,6 +76,9 @@ const MenuItem = defineComponent({
|
||||
},
|
||||
})
|
||||
/* const MenuItem = (props: any) => {
|
||||
const MenuItem = (props: any) => {
|
||||
const router = useRouter()
|
||||
|
||||
const { children, hidden, path } = props.menu
|
||||
if (hidden) {
|
||||
return null
|
||||
@@ -81,6 +87,9 @@ const MenuItem = defineComponent({
|
||||
return h(
|
||||
ElSubMenu,
|
||||
{ index: path, onClick: (e: any) => handleMenuClick(e) },
|
||||
{
|
||||
index: path,
|
||||
},
|
||||
{
|
||||
title: () => titleWithIcon(props),
|
||||
default: () => children.map((ele: any) => h(MenuItem, { menu: ele })),
|
||||
@@ -92,6 +101,12 @@ const MenuItem = defineComponent({
|
||||
return h(
|
||||
ElMenuItem,
|
||||
{ index: path, onClick: (e: any) => handleMenuClick(e) },
|
||||
{
|
||||
index: path,
|
||||
onClick: () => {
|
||||
router.push(path)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: h('span', null, { default: () => title }),
|
||||
default: h(iconCom, null, {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import Default_avatar from '@/assets/svg/icon-member-default.svg'
|
||||
import { ref, computed } from 'vue'
|
||||
import Default_avatar from '@/assets/workspace/default_avatar.png'
|
||||
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'
|
||||
@@ -34,11 +34,11 @@ const languageList = [
|
||||
value: 'en',
|
||||
},
|
||||
{
|
||||
name: '中文',
|
||||
name: t('common.simplified_chinese'),
|
||||
value: 'zh-CN',
|
||||
} /* ,
|
||||
{
|
||||
name: '繁體中文',
|
||||
name: t('common.traditional_chinese'),
|
||||
value: 'zh-CN',
|
||||
}, */,
|
||||
]
|
||||
@@ -74,18 +74,14 @@ const logout = () => {
|
||||
<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>
|
||||
<img class="default-avatar" :src="Default_avatar" width="32px" height="32px" />
|
||||
<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>
|
||||
<img :src="Default_avatar" width="40px" height="40px" />
|
||||
<div class="top">{{ name }}</div>
|
||||
<div class="bottom">{{ account }}</div>
|
||||
</div>
|
||||
@@ -93,13 +89,13 @@ const logout = () => {
|
||||
<el-icon size="16">
|
||||
<icon_admin_outlined></icon_admin_outlined>
|
||||
</el-icon>
|
||||
<div class="datasource-name">系统管理</div>
|
||||
<div class="datasource-name">{{ $t('common.system_manage') }}</div>
|
||||
</div>
|
||||
<div class="popover-item" @click="openPwd">
|
||||
<el-icon size="16">
|
||||
<icon_key_outlined></icon_key_outlined>
|
||||
</el-icon>
|
||||
<div class="datasource-name">修改密码</div>
|
||||
<div class="datasource-name">{{ $t('user.change_password') }}</div>
|
||||
</div>
|
||||
<el-popover :teleported="false" popper-class="system-language" placement="right">
|
||||
<template #reference>
|
||||
@@ -107,7 +103,7 @@ const logout = () => {
|
||||
<el-icon size="16">
|
||||
<icon_translate_outlined></icon_translate_outlined>
|
||||
</el-icon>
|
||||
<div class="datasource-name">语言</div>
|
||||
<div class="datasource-name">{{ $t('common.language') }}</div>
|
||||
<el-icon style="transform: scale(1.33)" class="right" size="16">
|
||||
<icon_right_outlined></icon_right_outlined>
|
||||
</el-icon>
|
||||
@@ -133,13 +129,13 @@ const logout = () => {
|
||||
<el-icon size="16">
|
||||
<icon_maybe_outlined></icon_maybe_outlined>
|
||||
</el-icon>
|
||||
<div class="datasource-name">帮助</div>
|
||||
<div class="datasource-name">{{ $t('common.help') }}</div>
|
||||
</div>
|
||||
<div class="popover-item mr4" @click="logout">
|
||||
<el-icon size="16">
|
||||
<icon_logout_outlined></icon_logout_outlined>
|
||||
</el-icon>
|
||||
<div class="datasource-name">退出登录</div>
|
||||
<div class="datasource-name">{{ $t('common.logout') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -158,7 +154,6 @@ const logout = () => {
|
||||
|
||||
<style lang="less" scoped>
|
||||
.person {
|
||||
border-radius: 6px;
|
||||
padding: 0 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -167,6 +162,7 @@ const logout = () => {
|
||||
height: 40px;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
position: relative;
|
||||
|
||||
&.collapse {
|
||||
min-width: 48px;
|
||||
@@ -190,15 +186,28 @@ const logout = () => {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:active {
|
||||
background: #1f23291a;
|
||||
border: 1px solid #1f23291a;
|
||||
&:focus {
|
||||
&::after {
|
||||
background: #1f23291a;
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: #1f232926;
|
||||
border: 1px solid #1f23291a;
|
||||
&::after {
|
||||
background: #1f232926;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -236,9 +245,9 @@ const logout = () => {
|
||||
height: 62px;
|
||||
padding: 8px;
|
||||
|
||||
.ed-icon {
|
||||
margin: 6px 8px 0 7px;
|
||||
img {
|
||||
float: left;
|
||||
margin: 3px 8px 0 7px;
|
||||
}
|
||||
|
||||
.top {
|
||||
|
||||
@@ -128,6 +128,8 @@ onMounted(async () => {
|
||||
|
||||
&.collapse {
|
||||
width: 40px;
|
||||
background: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.name {
|
||||
|
||||
@@ -17,7 +17,14 @@
|
||||
"save_success": "Save Successful",
|
||||
"next": "Next",
|
||||
"save": "Save",
|
||||
"logout": "Logout"
|
||||
"logout": "Logout",
|
||||
"result_count": "result",
|
||||
"clear_filter": "Clear conditions",
|
||||
"reset": "Reset",
|
||||
"simplified_chinese": "Simplified Chinese",
|
||||
"traditional_chinese": "Traditional Chinese",
|
||||
"help": "Help",
|
||||
"language": "Language"
|
||||
},
|
||||
"dashboard": {
|
||||
"select_dashboard": "Please Select Dashboard",
|
||||
@@ -75,6 +82,7 @@
|
||||
"actions": "Actions",
|
||||
"pieces_in_total": "Showing {ms} records out of {msg}",
|
||||
"test_connection": "Test Connection",
|
||||
"check": "check",
|
||||
"connection_success": "Connection Successful",
|
||||
"connection_failed": "Connection Failed, Please Check Configuration",
|
||||
"Search Datasource": "Search Datasource",
|
||||
@@ -168,6 +176,7 @@
|
||||
"field_name": "Field name",
|
||||
"relevant_content_found": "No relevant content found",
|
||||
"please_enter": "Please enter",
|
||||
"Please_select": "Please select",
|
||||
"table_notes": "Table notes",
|
||||
"field_notes": "Field notes",
|
||||
"select_all": "Select all",
|
||||
@@ -208,6 +217,83 @@
|
||||
"parameter_type": "Parameter type",
|
||||
"verification_successful": "Verification successful"
|
||||
},
|
||||
"user": {
|
||||
"workspace": "Workspace",
|
||||
"creation_time": "Creation time",
|
||||
"user_source": "User source",
|
||||
"phone_number": "Phone number",
|
||||
"email": "Email",
|
||||
"user_status": "User status",
|
||||
"account": "Account",
|
||||
"name": "Name",
|
||||
"selected_2_items": "Selected {msg} items",
|
||||
"name_account_email": "Search name, account, email",
|
||||
"user_management": "User management",
|
||||
"filter": "Filter",
|
||||
"batch_import": "Batch import",
|
||||
"add_users": "Add users",
|
||||
"selected_2_users": "Do you want to delete the selected 2 users?",
|
||||
"filter_conditions": "Filter conditions",
|
||||
"enable": "Enable",
|
||||
"disable": "Disable",
|
||||
"local_creation": "Local creation",
|
||||
"feishu": "Feishu",
|
||||
"dingtalk": "DingTalk",
|
||||
"wechat_for_business": "WeChat for business",
|
||||
"1240_results": "{msg} results",
|
||||
"clear_conditions": "Clear conditions",
|
||||
"disabled": "Disabled",
|
||||
"enabled": "Enabled",
|
||||
"edit_user": "Edit User",
|
||||
"del_user": "Do you want to delete user: {msg}?",
|
||||
"please_first": "Please first",
|
||||
"download_the_template": "download the template",
|
||||
"required_and_upload": ", fill in as required and upload",
|
||||
"file": "file",
|
||||
"upload_file": "Upload file",
|
||||
"xls_format_files": "Only supports xlsx and xls format files",
|
||||
"import": "Import",
|
||||
"change_file": "Change file",
|
||||
"data_import_completed": "Data import completed",
|
||||
"imported_100_data": "Successfully imported {msg} data",
|
||||
"return_to_view": "Return to view",
|
||||
"continue_importing": "Continue importing",
|
||||
"import_20_can": "Successfully imported {msg} data, failed to import {loss}, can",
|
||||
"download_error_report": "Download error report",
|
||||
"modify_and_re_import": ", modify and re-import",
|
||||
"data_import_failed": "Data import failed",
|
||||
"failed_100_can": "Import failed {msg}, can",
|
||||
"change_password": "Change password",
|
||||
"new_password": "New password",
|
||||
"confirm_password": "Confirm password",
|
||||
"old_password": "Old password"
|
||||
},
|
||||
"workspace": {
|
||||
"add_workspace": "Add workspace",
|
||||
"administrator": "Administrator",
|
||||
"ordinary_member": "Ordinary member",
|
||||
"people": "People",
|
||||
"name_username_email": "Search name, username, email",
|
||||
"add_member": "Add member",
|
||||
"member_type": "Member type",
|
||||
"workspace_name": "Workspace name",
|
||||
"no_user": "No user",
|
||||
"remove": "Remove",
|
||||
"selected_2_members": "Do you want to remove the selected {msg} members?",
|
||||
"removed_successfully": "Removed successfully",
|
||||
"workspace_de_workspace": "Do you want to delete the workspace: {msg}?",
|
||||
"member_feng_yibudao": "Do you want to remove the member: {msg}?",
|
||||
"select_member": "Select member",
|
||||
"selected_2_people": "Selected: {msg} people",
|
||||
"clear": "Clear",
|
||||
"historical_dialogue": "No historical dialogue",
|
||||
"rename_a_workspace": "Rename a workspace",
|
||||
"return_to_workspace": "Return to workspace",
|
||||
"there_are": "There are",
|
||||
"2_dashboards": "{msg} dashboards",
|
||||
"smart_data_centers": "{msg} smart data centers",
|
||||
"confirm_to_delete": "This data source is in use. Deleting it will cause these resources to not work properly. Confirm to delete?"
|
||||
},
|
||||
"chat": {
|
||||
"chart_type": {
|
||||
"table": "Table",
|
||||
|
||||
@@ -19,7 +19,14 @@
|
||||
"save": "保存",
|
||||
"logout": "退出登录",
|
||||
"please_input": "请输入{msg}",
|
||||
"switch_success": "切换成功"
|
||||
"switch_success": "切换成功",
|
||||
"result_count": "个结果",
|
||||
"clear_filter": "清空条件",
|
||||
"reset": "重置",
|
||||
"simplified_chinese": "简体中文",
|
||||
"traditional_chinese": "繁體中文",
|
||||
"help": "帮助",
|
||||
"language": "语言"
|
||||
},
|
||||
"dashboard": {
|
||||
"select_dashboard": "请选择仪表板",
|
||||
@@ -83,6 +90,7 @@
|
||||
"pieces_in_total": "显示 {ms} 条数据,共 {msg} 条",
|
||||
"actions": "操作",
|
||||
"test_connection": "测试连接",
|
||||
"check": "校验",
|
||||
"connection_success": "连接成功",
|
||||
"connection_failed": "连接失败,请检查配置",
|
||||
"Search Datasource": "搜索数据源",
|
||||
@@ -176,6 +184,7 @@
|
||||
"field_name": "字段名称",
|
||||
"relevant_content_found": "没有找到相关内容",
|
||||
"please_enter": "请输入",
|
||||
"Please_select": "请选择",
|
||||
"table_notes": "表备注",
|
||||
"field_notes": "字段备注",
|
||||
"select_all": "全选",
|
||||
@@ -219,6 +228,90 @@
|
||||
"del_default_warn": "该模型为系统默认模型,请先设置其他模型为系统默认模型,再删除此模型。",
|
||||
"del_warn_tip": "是否删除模型: {msg}?"
|
||||
},
|
||||
"user": {
|
||||
"workspace": "工作空间",
|
||||
"creation_time": "创建时间",
|
||||
"user_source": "用户来源",
|
||||
"phone_number": "手机号",
|
||||
"email": "邮箱",
|
||||
"user_status": "用户状态",
|
||||
"account": "账号",
|
||||
"name": "姓名",
|
||||
"selected_2_items": "已选 {msg} 条",
|
||||
"name_account_email": "搜索姓名、账号、邮箱",
|
||||
"user_management": "用户管理",
|
||||
"filter": "筛选",
|
||||
"batch_import": "批量导入",
|
||||
"add_users": "添加用户",
|
||||
"selected_2_users": "是否删除选中的 {msg} 个用户?",
|
||||
"filter_conditions": "筛选条件",
|
||||
"enable": "启用",
|
||||
"disable": "禁用",
|
||||
"local_creation": "本地创建",
|
||||
"feishu": "飞书",
|
||||
"dingtalk": "钉钉",
|
||||
"wechat_for_business": "企业微信",
|
||||
"1240_results": "{msg} 个结果 ",
|
||||
"clear_conditions": "清空条件",
|
||||
"disabled": "已禁用",
|
||||
"enabled": "已启用",
|
||||
"edit_user": "编辑用户",
|
||||
"del_user": "是否删除用户: {msg}?",
|
||||
"please_first": "请先",
|
||||
"download_the_template": "下载模板",
|
||||
"required_and_upload": ",按要求填写后上传",
|
||||
"file": "文件",
|
||||
"upload_file": "上传文件",
|
||||
"xls_format_files": "仅支持xlsx、xls格式的文件",
|
||||
"import": "导入",
|
||||
"change_file": "更换文件",
|
||||
"data_import_completed": "数据导入完成",
|
||||
"imported_100_data": "成功导入数据 {msg} 条",
|
||||
"return_to_view": "返回查看",
|
||||
"continue_importing": "继续导入",
|
||||
"import_20_can": "成功导入数据 {msg} 条,导入失败 {loss} 条,可 ",
|
||||
"download_error_report": "下载错误报告",
|
||||
"modify_and_re_import": ",修改后重新导入",
|
||||
"data_import_failed": "数据导入失败",
|
||||
"failed_100_can": "导入失败 {msg} 条,可",
|
||||
"change_password": "修改密码",
|
||||
"new_password": "新密码",
|
||||
"confirm_password": "确认密码",
|
||||
"old_password": "旧密码",
|
||||
"title": "用户管理",
|
||||
"upgrade_pwd": {
|
||||
"title": "修改密码",
|
||||
"old_pwd": "旧密码",
|
||||
"new_pwd": "新密码",
|
||||
"confirm_pwd": "确认密码"
|
||||
}
|
||||
},
|
||||
"workspace": {
|
||||
"add_workspace": "添加工作空间",
|
||||
"administrator": "管理员",
|
||||
"ordinary_member": "普通成员",
|
||||
"people": "人",
|
||||
"name_username_email": "搜索姓名、用户名、邮箱",
|
||||
"add_member": "添加成员",
|
||||
"member_type": "成员类型",
|
||||
"workspace_name": "工作空间名称",
|
||||
"no_user": "暂无用户",
|
||||
"remove": "移除",
|
||||
"selected_2_members": "是否移除选中的 {msg} 个成员?",
|
||||
"removed_successfully": "移除成功",
|
||||
"workspace_de_workspace": "是否删除工作空间:{msg}?",
|
||||
"member_feng_yibudao": "是否移除成员:{msg}?",
|
||||
"select_member": "选择成员",
|
||||
"selected_2_people": "已选:{msg} 人",
|
||||
"clear": "清空",
|
||||
"historical_dialogue": "暂无历史对话",
|
||||
"rename_a_workspace": "重命名工作空间",
|
||||
"return_to_workspace": "返回工作空间",
|
||||
"there_are": "有 ",
|
||||
"2_dashboards": "{msg} 个仪表板",
|
||||
"smart_data_centers": "{msg} 个智能问数",
|
||||
"confirm_to_delete": "正在使用此数据源,删除后会导致这些资源无法正常使用,确认删除?"
|
||||
},
|
||||
"chat": {
|
||||
"chart_type": {
|
||||
"table": "明细表",
|
||||
@@ -254,14 +347,5 @@
|
||||
"remark": "备注",
|
||||
"back_community": "还原至社区版",
|
||||
"confirm_tips": "确定还原至社区版?"
|
||||
},
|
||||
"user": {
|
||||
"title": "用户管理",
|
||||
"upgrade_pwd": {
|
||||
"title": "修改密码",
|
||||
"old_pwd": "旧密码",
|
||||
"new_pwd": "新密码",
|
||||
"confirm_pwd": "确认密码"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,8 @@ import Dashboard from '@/views/dashboard/index.vue'
|
||||
import Model from '@/views/system/model/Model.vue'
|
||||
import assistant from '@/views/embedded/assistant.vue'
|
||||
// import User from "@/views/system/user/index.vue";
|
||||
import User from '@/views/system/user/User.vue'
|
||||
import Workspace from '@/views/system/workspace/index.vue'
|
||||
import { watchRouter } from './watch'
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
@@ -121,6 +123,18 @@ const router = createRouter({
|
||||
component: LayoutDsl,
|
||||
redirect: '/system/model',
|
||||
children: [
|
||||
{
|
||||
path: 'user',
|
||||
name: 'user',
|
||||
component: User,
|
||||
meta: { title: '用户管理', icon: 'user' },
|
||||
},
|
||||
{
|
||||
path: 'workspace',
|
||||
name: 'workspace',
|
||||
component: Workspace,
|
||||
meta: { title: '工作空间', icon: 'workspace' },
|
||||
},
|
||||
{
|
||||
path: 'model',
|
||||
name: 'model',
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
--shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
|
||||
--ed-color-primary: #1cba90 !important;
|
||||
--ed-border-radius-base: 6px !important;
|
||||
}
|
||||
|
||||
a {
|
||||
@@ -149,3 +150,29 @@ body {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.ed-input__prefix-inner .ed-icon {
|
||||
color: #646a73;
|
||||
}
|
||||
|
||||
.confirm-no_icon {
|
||||
border-radius: 12px;
|
||||
padding: 24px;
|
||||
.tip {
|
||||
margin-top: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.form-content_error {
|
||||
.ed-form-item--default {
|
||||
margin-bottom: 16px;
|
||||
|
||||
&.is-error {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ed-dialog {
|
||||
border-radius: 12px !important;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { toType, createTypes, type VueTypesInterface, type VueTypeValidableDef } from 'vue-types'
|
||||
import type { CSSProperties } from 'vue'
|
||||
|
||||
// 自定义扩展vue-types
|
||||
type PropTypes = VueTypesInterface & {
|
||||
readonly style: VueTypeValidableDef<CSSProperties>
|
||||
}
|
||||
|
||||
const ProjectTypes = createTypes({
|
||||
func: undefined,
|
||||
bool: undefined,
|
||||
string: undefined,
|
||||
number: undefined,
|
||||
object: undefined,
|
||||
integer: undefined,
|
||||
}) as PropTypes
|
||||
|
||||
class propTypes extends ProjectTypes {
|
||||
style() {
|
||||
return toType('style', {
|
||||
type: [String, Object],
|
||||
default: undefined,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export { propTypes }
|
||||
@@ -12,8 +12,8 @@ const props = withDefaults(
|
||||
defineProps<{
|
||||
name: string
|
||||
type: string
|
||||
type_name: string
|
||||
rate?: string
|
||||
typeName: string
|
||||
num: string
|
||||
description?: string
|
||||
id?: string
|
||||
}>(),
|
||||
@@ -22,7 +22,7 @@ const props = withDefaults(
|
||||
type: '-',
|
||||
description: '-',
|
||||
id: '-',
|
||||
type_name: '-',
|
||||
typeName: '-',
|
||||
}
|
||||
)
|
||||
|
||||
@@ -58,7 +58,7 @@ const onClickOutside = () => {
|
||||
<img :src="icon" width="32px" height="32px" />
|
||||
<div class="info">
|
||||
<div class="name">{{ name }}</div>
|
||||
<div class="type">{{ type_name }}</div>
|
||||
<div class="type">{{ typeName }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="type-value">
|
||||
@@ -70,6 +70,7 @@ const onClickOutside = () => {
|
||||
<el-icon class="form-icon" size="16">
|
||||
<icon_form_outlined></icon_form_outlined>
|
||||
</el-icon>
|
||||
{{ num }}
|
||||
</div>
|
||||
<div click.stop class="methods">
|
||||
<el-button type="primary" style="margin-right: 8px" @click.stop="handleQuestion">
|
||||
|
||||
@@ -165,12 +165,17 @@ const changeStatus = (row: any) => {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const emits = defineEmits(['back'])
|
||||
const back = () => {
|
||||
emits('back')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="data-table no-padding">
|
||||
<div class="info">
|
||||
{{ $t('ds.title') }}
|
||||
<el-button text @click="back">{{ $t('ds.title') }}</el-button>
|
||||
<el-icon size="12">
|
||||
<icon_right_outlined></icon_right_outlined>
|
||||
</el-icon>
|
||||
@@ -206,6 +211,7 @@ const changeStatus = (row: any) => {
|
||||
:key="ele.table_name"
|
||||
class="model"
|
||||
:class="currentTable.table_name === ele.table_name && 'isActive'"
|
||||
:title="ele.table_name"
|
||||
@click="clickTable(ele)"
|
||||
>
|
||||
<el-icon size="16">
|
||||
@@ -353,8 +359,24 @@ const changeStatus = (row: any) => {
|
||||
color: #646a73;
|
||||
border-bottom: 1px solid #1f232926;
|
||||
|
||||
.ed-button {
|
||||
height: 22px;
|
||||
line-height: 22px;
|
||||
color: #646a73;
|
||||
|
||||
&:hover {
|
||||
background: #1cba901a;
|
||||
color: #1cba90;
|
||||
}
|
||||
&:active {
|
||||
color: #189e7a;
|
||||
background: #1cba9033;
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
color: #1f2329;
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
.content {
|
||||
@@ -372,7 +394,7 @@ const changeStatus = (row: any) => {
|
||||
justify-content: space-between;
|
||||
padding: 8px;
|
||||
|
||||
.ed-icon:hover {
|
||||
.ed-icon {
|
||||
cursor: pointer;
|
||||
color: var(--ed-color-primary);
|
||||
}
|
||||
@@ -392,11 +414,16 @@ const changeStatus = (row: any) => {
|
||||
padding-left: 8px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
|
||||
.name {
|
||||
margin-left: 8px;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
max-width: 80%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
&:hover {
|
||||
background: #1f23291a;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, shallowRef, nextTick } from 'vue'
|
||||
import { ref, computed, shallowRef, nextTick, h } 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'
|
||||
@@ -13,12 +13,15 @@ import DatasourceList from './DatasourceList.vue'
|
||||
import DatasourceListSide from './DatasourceListSide.vue'
|
||||
import DatasourceForm from './DatasourceForm.vue'
|
||||
import { datasourceApi } from '@/api/datasource'
|
||||
import { useEmitt } from '@/utils/useEmitt'
|
||||
import Card from './Card.vue'
|
||||
import { dsTypeWithImg } from './js/ds-type'
|
||||
import DelMessageBox from './DelMessageBox.vue'
|
||||
import { dsTypeWithImgSort } from './js/ds-type'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
interface Datasource {
|
||||
name: string
|
||||
num: string
|
||||
type_name: string
|
||||
type: string
|
||||
img: string
|
||||
@@ -34,20 +37,24 @@ const datasourceConfigvVisible = ref(false)
|
||||
const editDatasource = ref(false)
|
||||
const activeStep = ref(0)
|
||||
const activeName = ref('')
|
||||
const activeType = ref('')
|
||||
const datasourceFormRef = ref()
|
||||
|
||||
const datasourceList = shallowRef([] as Datasource[])
|
||||
const defaultDatasourceList = shallowRef(dsTypeWithImg as (Datasource & { img: string })[])
|
||||
const defaultDatasourceList = shallowRef(dsTypeWithImgSort 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())
|
||||
if (!keywords.value && !currentDatasourceType.value) return datasourceList.value
|
||||
return datasourceList.value.filter(
|
||||
(ele) =>
|
||||
ele.name.toLowerCase().includes(keywords.value.toLowerCase()) &&
|
||||
(ele.type === currentDatasourceType.value || !currentDatasourceType.value)
|
||||
)
|
||||
})
|
||||
const beforeClose = () => {
|
||||
datasourceConfigvVisible.value = false
|
||||
activeStep.value = 0
|
||||
}
|
||||
const defaultDatasourceListWithSearch = computed(() => {
|
||||
if (!defaultDatasourceKeywords.value) return defaultDatasourceList.value
|
||||
@@ -56,8 +63,16 @@ const defaultDatasourceListWithSearch = computed(() => {
|
||||
)
|
||||
})
|
||||
|
||||
const currentDatasourceType = ref('')
|
||||
|
||||
const handleDefaultDatasourceChange = (item: any) => {
|
||||
currentDefaultDatasource.value = item.name
|
||||
if (currentDatasourceType.value === item.type) {
|
||||
currentDefaultDatasource.value = ''
|
||||
currentDatasourceType.value = ''
|
||||
} else {
|
||||
currentDefaultDatasource.value = item.name
|
||||
currentDatasourceType.value = item.type
|
||||
}
|
||||
}
|
||||
|
||||
const formatKeywords = (item: string) => {
|
||||
@@ -92,43 +107,72 @@ const handleAddDatasource = () => {
|
||||
datasourceConfigvVisible.value = true
|
||||
}
|
||||
|
||||
const refresh = () => {
|
||||
activeName.value = ''
|
||||
activeStep.value = 0
|
||||
activeType.value = ''
|
||||
datasourceConfigvVisible.value = false
|
||||
search()
|
||||
}
|
||||
|
||||
const panelClick = () => {
|
||||
console.log('panelClick')
|
||||
}
|
||||
|
||||
const smartClick = () => {
|
||||
console.log('smartClick')
|
||||
}
|
||||
|
||||
const deleteHandler = (item: any) => {
|
||||
ElMessageBox.confirm(t('datasource.data_source', { msg: item.name }), {
|
||||
ElMessageBox.confirm('', {
|
||||
confirmButtonType: 'danger',
|
||||
tip: t('datasource.operate_with_caution'),
|
||||
confirmButtonText: t('dashboard.delete'),
|
||||
cancelButtonText: t('common.cancel'),
|
||||
customClass: 'confirm-no_icon',
|
||||
autofocus: false,
|
||||
dangerouslyUseHTMLString: true,
|
||||
message: h(
|
||||
DelMessageBox,
|
||||
{
|
||||
name: item.name,
|
||||
panelNum: 1,
|
||||
smartNum: 4,
|
||||
onPanelClick: panelClick,
|
||||
onSmartClick: smartClick,
|
||||
t,
|
||||
},
|
||||
''
|
||||
),
|
||||
}).then(() => {
|
||||
datasourceApi.delete(item.id).then(() => {
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: t('dashboard.delete_success'),
|
||||
})
|
||||
search()
|
||||
})
|
||||
})
|
||||
.then(() => {
|
||||
datasourceApi.delete(item.id).then(() => {
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: t('dashboard.delete_success'),
|
||||
})
|
||||
search()
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessageBox.confirm(t('datasource.data_source_de', { msg: item.name }), {
|
||||
tip: t('datasource.cannot_be_deleted'),
|
||||
cancelButtonText: t('datasource.got_it'),
|
||||
showConfirmButton: false,
|
||||
customClass: 'confirm-no_icon',
|
||||
autofocus: false,
|
||||
})
|
||||
})
|
||||
// .catch(() => {
|
||||
// ElMessageBox.confirm(t('datasource.data_source_de', { msg: item.name }), {
|
||||
// tip: t('datasource.cannot_be_deleted'),
|
||||
// cancelButtonText: t('datasource.got_it'),
|
||||
// showConfirmButton: false,
|
||||
// customClass: 'confirm-no_icon',
|
||||
// autofocus: false,
|
||||
// })
|
||||
// })
|
||||
}
|
||||
|
||||
const clickDatasource = (ele: any) => {
|
||||
activeStep.value = 1
|
||||
activeName.value = ele.name
|
||||
activeType.value = ele.type
|
||||
}
|
||||
|
||||
const clickDatasourceSide = (ele: any) => {
|
||||
activeName.value = ele.name
|
||||
activeStep.value = 1
|
||||
activeType.value = ele.type
|
||||
}
|
||||
|
||||
const search = () => {
|
||||
@@ -138,32 +182,15 @@ const search = () => {
|
||||
}
|
||||
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) => {
|
||||
useEmitt().emitter.emit('collapse-change')
|
||||
currentDataTable.value = ele
|
||||
}
|
||||
|
||||
const back = () => {
|
||||
currentDataTable.value = null
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -187,7 +214,7 @@ const dataTableDetail = (ele: any) => {
|
||||
<el-popover popper-class="system-default_datasource" placement="bottom">
|
||||
<template #reference>
|
||||
<el-button secondary>
|
||||
{{ $t('datasource.all_types') }}
|
||||
{{ currentDefaultDatasource || $t('datasource.all_types') }}
|
||||
<el-icon style="margin-left: 8px">
|
||||
<arrow_down></arrow_down>
|
||||
</el-icon> </el-button
|
||||
@@ -247,7 +274,8 @@ const dataTableDetail = (ele: any) => {
|
||||
:key="ele.id"
|
||||
:name="ele.name"
|
||||
:type="ele.type"
|
||||
:type_name="ele.type_name"
|
||||
:type-name="ele.type_name"
|
||||
:num="ele.num"
|
||||
:description="ele.description"
|
||||
@question="handleQuestion"
|
||||
@edit="handleEditDatasource(ele)"
|
||||
@@ -298,12 +326,13 @@ const dataTableDetail = (ele: any) => {
|
||||
ref="datasourceFormRef"
|
||||
:active-step="activeStep"
|
||||
:active-name="activeName"
|
||||
@submit="submit"
|
||||
:active-type="activeType"
|
||||
@refresh="refresh"
|
||||
@change-active-step="(val: number) => (activeStep = val)"
|
||||
></DatasourceForm>
|
||||
</el-drawer>
|
||||
</div>
|
||||
<DataTable v-if="currentDataTable" :info="currentDataTable"></DataTable>
|
||||
<DataTable v-if="currentDataTable" :info="currentDataTable" @back="back"></DataTable>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@@ -324,6 +353,8 @@ const dataTableDetail = (ele: any) => {
|
||||
.card-content {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
max-height: calc(100% - 40px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -16,17 +16,19 @@ import EmptyBackground from '@/views/dashboard/common/EmptyBackground.vue'
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
activeName: string
|
||||
activeType: string
|
||||
activeStep: number
|
||||
}>(),
|
||||
{
|
||||
activeName: '',
|
||||
activeType: '',
|
||||
activeStep: 0,
|
||||
}
|
||||
)
|
||||
|
||||
const { wsCache } = useCache()
|
||||
const dsFormRef = ref<FormInstance>()
|
||||
const emit = defineEmits(['refresh', 'submit', 'changeActiveStep'])
|
||||
const emit = defineEmits(['refresh', 'changeActiveStep'])
|
||||
const isCreate = ref(true)
|
||||
const isEditTable = ref(false)
|
||||
const checkList = ref<any>([])
|
||||
@@ -62,7 +64,7 @@ const dialogVisible = ref<boolean>(false)
|
||||
const form = ref<any>({
|
||||
name: '',
|
||||
description: '',
|
||||
type: 'mysql',
|
||||
type: props.activeType,
|
||||
configuration: '',
|
||||
driver: '',
|
||||
host: '',
|
||||
@@ -330,14 +332,6 @@ onMounted(() => {
|
||||
}, 100)
|
||||
})
|
||||
|
||||
const submitModle = () => {
|
||||
dsFormRef.value!.validate((res: any) => {
|
||||
if (res) {
|
||||
emit('submit')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const keywords = ref('')
|
||||
const tableListWithSearch = computed(() => {
|
||||
if (!keywords.value) return tableList.value
|
||||
@@ -354,6 +348,13 @@ watch(keywords, () => {
|
||||
isIndeterminate.value = checkedCount > 0 && checkedCount < tableListWithSearch.value.length
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.activeType,
|
||||
(val) => {
|
||||
form.value.type = val
|
||||
}
|
||||
)
|
||||
|
||||
const checkAll = ref(false)
|
||||
const isIndeterminate = ref(false)
|
||||
let checkTableList = [] as any[]
|
||||
@@ -390,7 +391,6 @@ const tableListSave = () => {
|
||||
|
||||
defineExpose({
|
||||
initForm,
|
||||
submitModle,
|
||||
tableListSave,
|
||||
})
|
||||
</script>
|
||||
@@ -555,15 +555,15 @@ defineExpose({
|
||||
</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 v-show="form.type !== 'excel'" @click="check">
|
||||
{{ t('ds.check') }}
|
||||
</el-button>
|
||||
<el-button v-show="activeStep !== 0 && isCreate" secondary @click="preview">
|
||||
{{ t('ds.previous') }}
|
||||
</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"
|
||||
@@ -642,7 +642,6 @@ defineExpose({
|
||||
}
|
||||
.container {
|
||||
height: 524px;
|
||||
overflow-y: auto;
|
||||
border: 1px solid #dee0e3;
|
||||
border-radius: 4px;
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ const handleModelClick = (item: any) => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="model-list">
|
||||
<div class="datasouce-list">
|
||||
<div class="title">{{ $t('qa.select_datasource') }}</div>
|
||||
<el-input
|
||||
v-model="keywords"
|
||||
@@ -61,7 +61,7 @@ const handleModelClick = (item: any) => {
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.model-list {
|
||||
.datasouce-list {
|
||||
width: 800px;
|
||||
margin: 0 auto;
|
||||
max-height: 100%;
|
||||
@@ -91,7 +91,7 @@ const handleModelClick = (item: any) => {
|
||||
padding-left: 16px;
|
||||
margin-bottom: 16px;
|
||||
border: 1px solid #dee0e3;
|
||||
border-radius: 4px;
|
||||
border-radius: 12px;
|
||||
margin-left: 16px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
<script lang="ts" setup>
|
||||
defineProps({
|
||||
name: {
|
||||
type: String,
|
||||
default: '-',
|
||||
},
|
||||
panelNum: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
smartNum: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
t: {
|
||||
type: Function,
|
||||
default: () => {},
|
||||
},
|
||||
})
|
||||
|
||||
const emits = defineEmits(['panelClick', 'smartClick'])
|
||||
const panelClick = () => {
|
||||
emits('panelClick')
|
||||
}
|
||||
|
||||
const smartClick = () => {
|
||||
emits('smartClick')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="del-box_message">
|
||||
<div class="del-title">
|
||||
{{ t('datasource.data_source', { msg: name }) }}
|
||||
</div>
|
||||
<div class="use-panel">
|
||||
{{ t('workspace.there_are') }}
|
||||
<span @click="panelClick">{{ t('workspace.2_dashboards', { msg: panelNum }) }}</span
|
||||
>、<span @click="smartClick">{{ t('workspace.smart_data_centers', { msg: smartNum }) }}</span>
|
||||
{{ t('workspace.confirm_to_delete') }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.del-box_message {
|
||||
.del-title {
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.use-panel {
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
margin-top: 24px;
|
||||
|
||||
span {
|
||||
text-decoration: underline;
|
||||
text-decoration-style: solid;
|
||||
cursor: pointer;
|
||||
color: #1cba90;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -135,7 +135,7 @@
|
||||
<div style="display: flex; justify-content: flex-end; margin-top: 20px">
|
||||
<el-button @click="close">{{ t('common.cancel') }}</el-button>
|
||||
<el-button v-show="!isCreate && !isEditTable && form.type !== 'excel'" @click="check">
|
||||
{{ t('ds.test_connection') }}
|
||||
{{ t('ds.check') }}
|
||||
</el-button>
|
||||
<el-button v-show="active === 0 && isCreate" type="primary" @click="next(dsFormRef)">
|
||||
{{ t('common.next') }}
|
||||
|
||||
@@ -13,11 +13,19 @@ export const dsType = [
|
||||
]
|
||||
|
||||
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 },
|
||||
{ name: 'MySQL', type: 'mysql', img: mysql_ds },
|
||||
{ name: 'SQL Server', type: 'sqlServer', img: sqlServer },
|
||||
{ name: 'Oracle', type: 'oracle', img: oracle },
|
||||
{ name: 'PostgreSQL', type: 'pg', img: pg },
|
||||
]
|
||||
|
||||
export const dsTypeWithImgSort = [
|
||||
{ name: '本地 Excel/CSV', type: 'excel', img: excel },
|
||||
{ name: 'PostgreSQL', type: 'pg', img: pg },
|
||||
{ name: 'Oracle', type: 'oracle', img: oracle },
|
||||
{ name: 'MySQL', type: 'mysql', img: mysql_ds },
|
||||
{ name: 'SQL Server', type: 'sqlServer', img: sqlServer },
|
||||
]
|
||||
|
||||
export const haveSchema = ['sqlServer', 'pg', 'oracle']
|
||||
|
||||
@@ -30,7 +30,7 @@ const current_supplier = computed(() => {
|
||||
const emits = defineEmits(['edit', 'del'])
|
||||
|
||||
const handleEdit = () => {
|
||||
emits('edit', props.id)
|
||||
emits('edit')
|
||||
}
|
||||
|
||||
const handleDel = () => {
|
||||
|
||||
@@ -32,6 +32,7 @@ const modelConfigvVisible = ref(false)
|
||||
const editModel = ref(false)
|
||||
const activeStep = ref(0)
|
||||
const activeName = ref('')
|
||||
const activeType = ref('')
|
||||
const modelFormRef = ref()
|
||||
|
||||
reactive({
|
||||
@@ -102,9 +103,11 @@ const handleAddModel = () => {
|
||||
activeStep.value = 0
|
||||
modelConfigvVisible.value = true
|
||||
}
|
||||
const handleEditModel = (id: any) => {
|
||||
const handleEditModel = (row: any) => {
|
||||
activeStep.value = 1
|
||||
modelApi.query(id).then((res: any) => {
|
||||
activeType.value = row.supplier
|
||||
activeName.value = row.supplier_item.name
|
||||
modelApi.query(row.id).then((res: any) => {
|
||||
modelConfigvVisible.value = true
|
||||
nextTick(() => {
|
||||
modelFormRef.value.initForm({ ...res })
|
||||
@@ -287,7 +290,7 @@ const submit = (item: any) => {
|
||||
:modle-type="getModelTypeName(ele['model_type'])"
|
||||
:base-modle="ele['base_model']"
|
||||
:is-default="ele['default_model']"
|
||||
@edit="handleEditModel"
|
||||
@edit="handleEditModel(ele)"
|
||||
@del="deleteHandler"
|
||||
></card>
|
||||
</div>
|
||||
@@ -320,12 +323,14 @@ const submit = (item: any) => {
|
||||
<ModelListSide
|
||||
v-if="activeStep === 1"
|
||||
:active-name="activeName"
|
||||
:active-type="activeType"
|
||||
@click-model="supplierChang"
|
||||
></ModelListSide>
|
||||
<ModelForm
|
||||
v-if="activeStep === 1"
|
||||
ref="modelFormRef"
|
||||
:active-name="activeName"
|
||||
:active-type="activeType"
|
||||
@submit="submit"
|
||||
></ModelForm>
|
||||
<template #footer>
|
||||
@@ -354,6 +359,8 @@ const submit = (item: any) => {
|
||||
|
||||
.card-content {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
max-height: calc(100% - 40px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -37,7 +37,7 @@ const modelForm = reactive({
|
||||
api_domain: '',
|
||||
config_list: [],
|
||||
})
|
||||
let isCreate = false
|
||||
const isCreate = ref(false)
|
||||
const modelRef = ref()
|
||||
const paramsFormRef = ref()
|
||||
const advancedSetting = ref([] as ParamsFormData[])
|
||||
@@ -51,7 +51,7 @@ const modelList = computed(() => {
|
||||
return base_model_options(modelForm.supplier, modelForm.model_type)
|
||||
})
|
||||
const handleParamsEdite = (ele?: any) => {
|
||||
isCreate = false
|
||||
isCreate.value = false
|
||||
paramsFormDrawer.value = true
|
||||
nextTick(() => {
|
||||
paramsFormRef.value.initForm(ele)
|
||||
@@ -59,7 +59,7 @@ const handleParamsEdite = (ele?: any) => {
|
||||
}
|
||||
|
||||
const handleParamsCreate = () => {
|
||||
isCreate = true
|
||||
isCreate.value = true
|
||||
paramsFormDrawer.value = true
|
||||
nextTick(() => {
|
||||
paramsFormRef.value.initForm()
|
||||
@@ -67,7 +67,7 @@ const handleParamsCreate = () => {
|
||||
}
|
||||
|
||||
const handleParamsDel = (item: any) => {
|
||||
advancedSetting.value = advancedSetting.value.filter((ele) => ele.key !== item.key)
|
||||
advancedSetting.value = advancedSetting.value.filter((ele) => ele.id !== item.id)
|
||||
}
|
||||
|
||||
const rules = {
|
||||
@@ -107,9 +107,9 @@ const addParams = () => {
|
||||
}
|
||||
|
||||
const submit = (item: any) => {
|
||||
if (isCreate) {
|
||||
if (isCreate.value) {
|
||||
advancedSetting.value.push({ ...item })
|
||||
cancel()
|
||||
beforeClose()
|
||||
return
|
||||
}
|
||||
for (const key in advancedSetting.value) {
|
||||
@@ -119,16 +119,18 @@ const submit = (item: any) => {
|
||||
}
|
||||
}
|
||||
|
||||
cancel()
|
||||
beforeClose()
|
||||
}
|
||||
|
||||
const cancel = () => {
|
||||
const beforeClose = () => {
|
||||
paramsFormRef.value.close()
|
||||
paramsFormDrawer.value = false
|
||||
}
|
||||
const supplierChang = (supplier: any) => {
|
||||
modelForm.supplier = supplier.id
|
||||
const config = supplier.model_config[modelForm.model_type || 0]
|
||||
modelForm.api_domain = config.api_domain
|
||||
modelForm.base_model = ''
|
||||
if (!modelForm.id) {
|
||||
modelForm.name = supplier.name
|
||||
}
|
||||
@@ -283,12 +285,19 @@ defineExpose({
|
||||
<el-drawer
|
||||
v-model="paramsFormDrawer"
|
||||
:size="600"
|
||||
:title="$t('model.add') + $t('common.empty') + $t('model.parameters')"
|
||||
:before-close="beforeClose"
|
||||
:title="
|
||||
isCreate
|
||||
? $t('model.add') + $t('common.empty') + $t('model.parameters')
|
||||
: $t('datasource.edit') + $t('common.empty') + $t('model.parameters')
|
||||
"
|
||||
>
|
||||
<ParamsForm ref="paramsFormRef" @submit="submit"></ParamsForm>
|
||||
<template #footer>
|
||||
<el-button secondary @click="cancel"> {{ $t('common.cancel') }} </el-button>
|
||||
<el-button type="primary" @click="addParams"> {{ t('model.add') }} </el-button>
|
||||
<el-button secondary @click="beforeClose"> {{ $t('common.cancel') }} </el-button>
|
||||
<el-button type="primary" @click="addParams">
|
||||
{{ isCreate ? t('model.add') : t('common.save') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</div>
|
||||
@@ -316,6 +325,8 @@ defineExpose({
|
||||
width: 800px;
|
||||
margin: 0 auto;
|
||||
padding-top: 24px;
|
||||
overflow-y: auto;
|
||||
height: calc(100% - 180px);
|
||||
|
||||
.ed-form-item--default {
|
||||
margin-bottom: 16px;
|
||||
|
||||
@@ -67,9 +67,8 @@ const handleModelClick = (item: any) => {
|
||||
.list-content {
|
||||
margin-top: 16px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
height: calc(100% - 40px);
|
||||
gap: 16px;
|
||||
flex-wrap: wrap;
|
||||
.model:nth-child(odd) {
|
||||
margin-left: 0;
|
||||
}
|
||||
@@ -81,10 +80,9 @@ 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;
|
||||
border-radius: 12px;
|
||||
margin-left: 16px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
box-shadow: 0px 6px 24px 0px #1f232914;
|
||||
|
||||
@@ -8,6 +8,7 @@ const paramsForm = reactive({
|
||||
name: '',
|
||||
key: '',
|
||||
val: '',
|
||||
id: '',
|
||||
})
|
||||
|
||||
const rules = {
|
||||
@@ -38,6 +39,9 @@ const initForm = (item: any) => {
|
||||
if (item) {
|
||||
Object.assign(paramsForm, { ...item })
|
||||
}
|
||||
if (!paramsForm.id) {
|
||||
paramsForm.id = `${+new Date()}`
|
||||
}
|
||||
paramsRef.value.clearValidate()
|
||||
}
|
||||
|
||||
@@ -50,9 +54,17 @@ const submit = () => {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const close = () => {
|
||||
paramsForm.name = ''
|
||||
paramsForm.key = ''
|
||||
paramsForm.val = ''
|
||||
paramsForm.id = ''
|
||||
}
|
||||
defineExpose({
|
||||
initForm,
|
||||
submit,
|
||||
close,
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -0,0 +1,773 @@
|
||||
<template>
|
||||
<div class="sqlbot-table-container professional-container">
|
||||
<div class="tool-left">
|
||||
<span class="page-title">{{ $t('user.user_management') }}</span>
|
||||
<div class="search-bar">
|
||||
<el-input
|
||||
v-model="keyword"
|
||||
style="width: 240px; margin-right: 12px"
|
||||
:placeholder="$t('user.name_account_email')"
|
||||
@keyup.enter="handleSearch"
|
||||
@blur="handleSearch"
|
||||
>
|
||||
<template #prefix>
|
||||
<el-icon>
|
||||
<icon_searchOutline_outlined />
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
|
||||
<el-button secondary @click="drawerMainOpen">
|
||||
<template #icon>
|
||||
<iconFilter></iconFilter>
|
||||
</template>
|
||||
{{ $t('user.filter') }}
|
||||
</el-button>
|
||||
<el-button secondary @click="handleUserImport">
|
||||
<template #icon>
|
||||
<ccmUpload></ccmUpload>
|
||||
</template>
|
||||
{{ $t('user.batch_import') }}
|
||||
</el-button>
|
||||
<el-button type="primary" @click="editHandler(null)">
|
||||
<template #icon>
|
||||
<icon_add_outlined></icon_add_outlined>
|
||||
</template>
|
||||
{{ $t('user.add_users') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sqlbot-table" :class="state.filterTexts.length && 'is-filter'">
|
||||
<filter-text
|
||||
:total="state.pageInfo.total"
|
||||
:filter-texts="state.filterTexts"
|
||||
@clear-filter="clearFilter"
|
||||
/>
|
||||
<el-table
|
||||
ref="multipleTableRef"
|
||||
:data="state.tableData"
|
||||
style="width: 100%"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column prop="name" :label="$t('user.name')" width="280" />
|
||||
<el-table-column prop="account" :label="$t('user.account')" width="280" />
|
||||
<el-table-column prop="status" :label="$t('user.user_status')" width="180">
|
||||
<template #default="scope">
|
||||
<div class="user-status-container" :class="[scope.row.status ? 'active' : 'disabled']">
|
||||
<el-icon size="16">
|
||||
<SuccessFilled v-if="scope.row.status" />
|
||||
<CircleCloseFilled v-else />
|
||||
</el-icon>
|
||||
<span>{{ $t(`user.${scope.row.status ? 'enabled' : 'disabled'}`) }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="email" :label="$t('user.email')" width="280" />
|
||||
<!-- <el-table-column prop="phone" :label="$t('user.phone_number')" width="280" /> -->
|
||||
<!-- <el-table-column prop="user_source" :label="$t('user.user_source')" width="280" /> -->
|
||||
<el-table-column prop="space_name" :label="$t('user.workspace')" width="280" />
|
||||
|
||||
<el-table-column prop="create_time" width="180" sortable :label="$t('user.creation_time')">
|
||||
<template #default="scope">
|
||||
<span>{{ formatTimestamp(scope.row.create_time) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column fixed="right" width="150" :label="$t('ds.actions')">
|
||||
<template #default="scope">
|
||||
<div class="table-operate">
|
||||
<el-switch
|
||||
v-model="scope.row.status"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
size="small"
|
||||
@change="statusHandler(scope.row)"
|
||||
/>
|
||||
<div class="line"></div>
|
||||
<el-tooltip
|
||||
:offset="14"
|
||||
effect="dark"
|
||||
:content="$t('datasource.edit')"
|
||||
placement="top"
|
||||
>
|
||||
<el-icon class="action-btn" size="16" @click="editHandler(scope.row)">
|
||||
<IconOpeEdit></IconOpeEdit>
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip
|
||||
:offset="14"
|
||||
effect="dark"
|
||||
:content="$t('user.change_password')"
|
||||
placement="top"
|
||||
>
|
||||
<el-icon class="action-btn" size="16" @click="handleEditPassword(scope.row.id)">
|
||||
<IconLock></IconLock>
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip
|
||||
:offset="14"
|
||||
effect="dark"
|
||||
:content="$t('dashboard.delete')"
|
||||
placement="top"
|
||||
>
|
||||
<el-icon class="action-btn" size="16" @click="deleteHandler(scope.row)">
|
||||
<IconOpeDelete></IconOpeDelete>
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="pagination-container">
|
||||
<el-pagination
|
||||
v-model:current-page="state.pageInfo.currentPage"
|
||||
v-model:page-size="state.pageInfo.pageSize"
|
||||
:page-sizes="[10, 20, 30]"
|
||||
:background="true"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="state.pageInfo.total"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="multipleSelectionAll.length" class="bottom-select">
|
||||
<el-checkbox
|
||||
v-model="checkAll"
|
||||
:indeterminate="isIndeterminate"
|
||||
@change="handleCheckAllChange"
|
||||
>
|
||||
{{ $t('datasource.select_all') }}
|
||||
</el-checkbox>
|
||||
|
||||
<button class="danger-button" @click="deleteBatchUser">{{ $t('dashboard.delete') }}</button>
|
||||
|
||||
<span class="selected">{{
|
||||
$t('user.selected_2_items', { msg: multipleSelectionAll.length })
|
||||
}}</span>
|
||||
|
||||
<el-button text @click="cancelDelete">
|
||||
{{ $t('common.cancel') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-drawer
|
||||
v-model="dialogFormVisible"
|
||||
:title="dialogTitle"
|
||||
destroy-on-close
|
||||
size="600px"
|
||||
modle-class="sqlbot-drawer"
|
||||
@close="onFormClose"
|
||||
>
|
||||
<el-form
|
||||
ref="termFormRef"
|
||||
:model="state.form"
|
||||
label-width="180px"
|
||||
label-position="top"
|
||||
:rules="rules"
|
||||
class="form-content_error"
|
||||
>
|
||||
<el-form-item prop="name" :label="t('user.name')">
|
||||
<el-input
|
||||
v-model="state.form.name"
|
||||
:placeholder="$t('datasource.please_enter') + $t('common.empty') + $t('user.name')"
|
||||
autocomplete="off"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item prop="account" :label="t('user.account')">
|
||||
<el-input
|
||||
v-model="state.form.account"
|
||||
:placeholder="$t('datasource.please_enter') + $t('common.empty') + $t('user.account')"
|
||||
autocomplete="off"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item prop="email" :label="$t('user.email')">
|
||||
<el-input
|
||||
v-model="state.form.email"
|
||||
:placeholder="$t('datasource.please_enter') + $t('common.empty') + $t('user.email')"
|
||||
autocomplete="off"
|
||||
/>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item :label="$t('user.phone_number')">
|
||||
<el-input
|
||||
v-model="state.form.phoneNumber"
|
||||
:placeholder="
|
||||
$t('datasource.please_enter') + $t('common.empty') + $t('user.phone_number')
|
||||
"
|
||||
autocomplete="off"
|
||||
/>
|
||||
</el-form-item> -->
|
||||
|
||||
<el-form-item :label="$t('user.workspace')">
|
||||
<el-select
|
||||
v-model="state.form.oid"
|
||||
:placeholder="$t('datasource.Please_select') + $t('common.empty') + $t('user.workspace')"
|
||||
>
|
||||
<el-option v-for="item in options" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('user.user_status')">
|
||||
<el-switch v-model="state.form.status" :active-value="1" :inactive-value="0" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="closeForm">{{ $t('common.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="saveHandler"> {{ $t('model.add') }} </el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-drawer>
|
||||
<el-dialog
|
||||
v-model="dialogVisiblePassword"
|
||||
:title="$t('user.change_password')"
|
||||
width="500"
|
||||
:before-close="handleClosePassword"
|
||||
>
|
||||
<el-form
|
||||
ref="passwordRef"
|
||||
:model="password"
|
||||
label-width="180px"
|
||||
label-position="top"
|
||||
:rules="passwordRules"
|
||||
class="form-content_error"
|
||||
>
|
||||
<el-form-item prop="new" :label="t('user.new_password')">
|
||||
<el-input
|
||||
v-model="password.new"
|
||||
:placeholder="
|
||||
$t('datasource.please_enter') + $t('common.empty') + $t('user.new_password')
|
||||
"
|
||||
autocomplete="off"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item prop="old" :label="t('user.confirm_password')">
|
||||
<el-input
|
||||
v-model="password.old"
|
||||
:placeholder="
|
||||
$t('datasource.please_enter') + $t('common.empty') + $t('user.confirm_password')
|
||||
"
|
||||
autocomplete="off"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="handleClosePassword">{{ $t('common.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="handleConfirmPassword">
|
||||
{{ $t('common.save') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<UserImport ref="userImportRef"></UserImport>
|
||||
<drawer-main
|
||||
ref="drawerMainRef"
|
||||
:filter-options="filterOption"
|
||||
@trigger-filter="searchCondition"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted, nextTick } from 'vue'
|
||||
import UserImport from './UserImport.vue'
|
||||
import SuccessFilled from '@/assets/svg/gou_icon.svg'
|
||||
import CircleCloseFilled from '@/assets/svg/icon_ban_filled.svg'
|
||||
import icon_searchOutline_outlined from '@/assets/svg/icon_search-outline_outlined.svg'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { convertFilterText, FilterText } from '@/components/filter-text'
|
||||
|
||||
import IconLock from '@/assets/svg/icon-key_outlined.svg'
|
||||
import IconOpeEdit from '@/assets/svg/icon_edit_outlined.svg'
|
||||
import IconOpeDelete from '@/assets/svg/icon_delete.svg'
|
||||
import iconFilter from '@/assets/svg/icon-filter_outlined.svg'
|
||||
import ccmUpload from '@/assets/svg/icon_ccm-upload_outlined.svg'
|
||||
import icon_add_outlined from '@/assets/svg/icon_add_outlined.svg'
|
||||
import { userApi } from '@/api/user'
|
||||
import { workspaceList } from '@/api/workspace'
|
||||
import { formatTimestamp } from '@/utils/date'
|
||||
|
||||
const { t } = useI18n()
|
||||
const keyword = ref('')
|
||||
const dialogFormVisible = ref(false)
|
||||
const termFormRef = ref()
|
||||
const checkAll = ref(false)
|
||||
const dialogVisiblePassword = ref(false)
|
||||
const isIndeterminate = ref(true)
|
||||
const drawerMainRef = ref()
|
||||
const userImportRef = ref()
|
||||
const filterOption = ref<any[]>([
|
||||
{
|
||||
type: 'enum',
|
||||
option: [
|
||||
{ id: 1, name: t('user.enable') },
|
||||
{ id: 0, name: t('user.disable') },
|
||||
],
|
||||
field: 'status',
|
||||
title: t('user.user_status'),
|
||||
operate: 'in',
|
||||
},
|
||||
{
|
||||
type: 'enum',
|
||||
option: [
|
||||
{ id: '0', name: t('user.local_creation') },
|
||||
{ id: 1, name: 'LDAP' },
|
||||
{ id: 2, name: 'OIDC' },
|
||||
{ id: 3, name: 'CAS' },
|
||||
{ id: 9, name: 'OAuth2' },
|
||||
{ id: 4, name: t('user.feishu') },
|
||||
{ id: 5, name: t('user.dingtalk') },
|
||||
{ id: 6, name: t('user.wechat_for_business') },
|
||||
],
|
||||
field: 'origins',
|
||||
title: t('user.user_source'),
|
||||
operate: 'in',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
option: [],
|
||||
field: 'oidist',
|
||||
title: t('user.workspace'),
|
||||
operate: 'in',
|
||||
property: { placeholder: t('common.empty') + t('user.workspace') },
|
||||
},
|
||||
])
|
||||
const options = ref<any[]>([])
|
||||
const state = reactive<any>({
|
||||
tableData: [],
|
||||
filterTexts: [],
|
||||
conditions: [],
|
||||
form: {
|
||||
id: '',
|
||||
name: '',
|
||||
account: '',
|
||||
oid: '',
|
||||
email: '',
|
||||
status: '',
|
||||
phoneNumber: '',
|
||||
},
|
||||
pageInfo: {
|
||||
currentPage: 1,
|
||||
pageSize: 20,
|
||||
total: 0,
|
||||
},
|
||||
})
|
||||
const rules = {
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
message: t('datasource.please_enter') + t('common.empty') + t('user.name'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
account: [
|
||||
{
|
||||
required: true,
|
||||
message: t('datasource.please_enter') + t('common.empty') + t('user.account'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
email: [
|
||||
{
|
||||
required: true,
|
||||
message: t('datasource.please_enter') + t('common.empty') + t('user.email'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const passwordRules = {
|
||||
new: [
|
||||
{
|
||||
required: true,
|
||||
message: t('datasource.please_enter') + t('common.empty') + t('user.new_password'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
old: [
|
||||
{
|
||||
required: true,
|
||||
message: t('datasource.please_enter') + t('common.empty') + t('user.confirm_password'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const multipleTableRef = ref()
|
||||
const multipleSelectionAll = ref<any[]>([])
|
||||
const dialogTitle = ref('')
|
||||
const passwordRef = ref()
|
||||
const password = ref({
|
||||
new: '',
|
||||
old: '',
|
||||
id: '',
|
||||
})
|
||||
|
||||
const handleClosePassword = () => {
|
||||
dialogVisiblePassword.value = false
|
||||
}
|
||||
|
||||
const handleEditPassword = (id: any) => {
|
||||
password.value.id = id
|
||||
dialogVisiblePassword.value = true
|
||||
}
|
||||
|
||||
const handleUserImport = () => {
|
||||
userImportRef.value.showDialog()
|
||||
}
|
||||
|
||||
const handleConfirmPassword = () => {
|
||||
passwordRef.value.validate((val: any) => {
|
||||
if (val) {
|
||||
console.log(val)
|
||||
}
|
||||
})
|
||||
dialogVisiblePassword.value = false
|
||||
}
|
||||
const handleSelectionChange = (val: any[]) => {
|
||||
const ids = state.tableData.map((ele: any) => ele.id)
|
||||
multipleSelectionAll.value = [
|
||||
...multipleSelectionAll.value.filter((ele) => !ids.includes(ele.id)),
|
||||
...val,
|
||||
]
|
||||
isIndeterminate.value = !(val.length === 0 || val.length === state.tableData.length)
|
||||
checkAll.value = val.length === state.tableData.length
|
||||
}
|
||||
const handleCheckAllChange = (val: any) => {
|
||||
isIndeterminate.value = false
|
||||
handleSelectionChange(val ? state.tableData : [])
|
||||
if (val) {
|
||||
handleToggleRowSelection()
|
||||
} else {
|
||||
multipleTableRef.value.clearSelection()
|
||||
}
|
||||
}
|
||||
|
||||
const handleToggleRowSelection = (check: boolean = true) => {
|
||||
let i = 0
|
||||
const ids = multipleSelectionAll.value.map((ele: any) => ele.id)
|
||||
for (const key in state.tableData) {
|
||||
if (ids.includes((state.tableData[key] as any).id)) {
|
||||
i += 1
|
||||
multipleTableRef.value.toggleRowSelection(state.tableData[key], check)
|
||||
}
|
||||
}
|
||||
checkAll.value = i === state.tableData.length
|
||||
isIndeterminate.value = !(i === 0 || i === state.tableData.length)
|
||||
}
|
||||
const handleSearch = () => {
|
||||
search()
|
||||
}
|
||||
const fillFilterText = () => {
|
||||
const textArray = state.conditions?.length
|
||||
? convertFilterText(state.conditions, filterOption.value)
|
||||
: []
|
||||
state.filterTexts = [...textArray]
|
||||
Object.assign(state.filterTexts, textArray)
|
||||
}
|
||||
const clearFilter = (params?: number) => {
|
||||
let index = params ? params : 0
|
||||
if (isNaN(index)) {
|
||||
state.filterTexts = []
|
||||
} else {
|
||||
state.filterTexts.splice(index, 1)
|
||||
}
|
||||
drawerMainRef.value.clearFilter(index)
|
||||
}
|
||||
const searchCondition = (conditions: any) => {
|
||||
state.conditions = conditions
|
||||
fillFilterText()
|
||||
search()
|
||||
drawerMainClose()
|
||||
}
|
||||
const drawerMainOpen = async () => {
|
||||
drawerMainRef.value.init()
|
||||
}
|
||||
const drawerMainClose = () => {
|
||||
drawerMainRef.value.close()
|
||||
}
|
||||
const editHandler = (row: any) => {
|
||||
if (row) {
|
||||
state.form = { ...row }
|
||||
}
|
||||
dialogFormVisible.value = true
|
||||
dialogTitle.value = row?.id ? t('user.edit_user') : t('user.add_users')
|
||||
}
|
||||
|
||||
const statusHandler = (row: any) => {
|
||||
state.form = { ...row }
|
||||
editTerm()
|
||||
}
|
||||
|
||||
const cancelDelete = () => {
|
||||
multipleSelectionAll.value = []
|
||||
checkAll.value = false
|
||||
isIndeterminate.value = false
|
||||
}
|
||||
const deleteBatchUser = () => {
|
||||
ElMessageBox.confirm(t('user.selected_2_users', { msg: multipleSelectionAll.value.length }), {
|
||||
confirmButtonType: 'danger',
|
||||
confirmButtonText: t('dashboard.delete'),
|
||||
cancelButtonText: t('common.cancel'),
|
||||
customClass: 'confirm-no_icon',
|
||||
autofocus: false,
|
||||
callback: (val: string) => {
|
||||
console.log(val)
|
||||
},
|
||||
})
|
||||
}
|
||||
const deleteHandler = (row: any) => {
|
||||
ElMessageBox.confirm(t('user.del_user', { msg: row.name }), {
|
||||
confirmButtonType: 'danger',
|
||||
confirmButtonText: t('dashboard.delete'),
|
||||
cancelButtonText: t('common.cancel'),
|
||||
customClass: 'confirm-no_icon',
|
||||
autofocus: false,
|
||||
})
|
||||
.then(() => {
|
||||
userApi.delete(row.id).then(() => {
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: 'Delete completed',
|
||||
})
|
||||
search()
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage({
|
||||
type: 'info',
|
||||
message: 'Delete canceled',
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/* const openDialog = () => {
|
||||
dialogFormVisible.value = true
|
||||
} */
|
||||
const resetForm = () => {
|
||||
if (!termFormRef.value) return
|
||||
Object.keys(state.form).forEach((key) => {
|
||||
state.form[key as keyof typeof state.form] = ''
|
||||
})
|
||||
}
|
||||
|
||||
const closeForm = () => {
|
||||
dialogFormVisible.value = false
|
||||
}
|
||||
const onFormClose = () => {
|
||||
resetForm()
|
||||
dialogFormVisible.value = false
|
||||
}
|
||||
|
||||
const configParams = () => {
|
||||
let str = ''
|
||||
if (keyword.value) {
|
||||
str += `keyword=${keyword.value}`
|
||||
}
|
||||
|
||||
state.conditions.forEach((ele: any) => {
|
||||
ele.value.forEach((itx: any) => {
|
||||
str += str ? `&${ele.field}=${itx}` : `${ele.field}=${itx}`
|
||||
})
|
||||
})
|
||||
|
||||
if (str.length) {
|
||||
str = `?${str}`
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
const search = () => {
|
||||
userApi
|
||||
.pager(configParams(), state.pageInfo.currentPage, state.pageInfo.pageSize)
|
||||
.then((res: any) => {
|
||||
state.tableData = res.items
|
||||
state.pageInfo.total = res.total
|
||||
|
||||
nextTick(() => {
|
||||
handleToggleRowSelection()
|
||||
})
|
||||
})
|
||||
}
|
||||
const addTerm = () => {
|
||||
userApi.add(state.form).then(() => {
|
||||
dialogFormVisible.value = false
|
||||
search()
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: t('common.save_success'),
|
||||
})
|
||||
})
|
||||
}
|
||||
const editTerm = () => {
|
||||
userApi.edit(state.form).then(() => {
|
||||
dialogFormVisible.value = false
|
||||
search()
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: t('common.save_success'),
|
||||
})
|
||||
})
|
||||
}
|
||||
const saveHandler = () => {
|
||||
if (state.form.id) {
|
||||
editTerm()
|
||||
} else {
|
||||
addTerm()
|
||||
}
|
||||
}
|
||||
const handleSizeChange = (val: number) => {
|
||||
state.pageInfo.pageSize = val
|
||||
search()
|
||||
}
|
||||
const handleCurrentChange = (val: number) => {
|
||||
state.pageInfo.currentPage = val
|
||||
search()
|
||||
}
|
||||
onMounted(() => {
|
||||
workspaceList().then((res) => {
|
||||
options.value = res || []
|
||||
filterOption.value[2].option = [...options.value]
|
||||
})
|
||||
search()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.sqlbot-table-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
.bottom-select {
|
||||
position: absolute;
|
||||
height: 64px;
|
||||
width: calc(100% + 48px);
|
||||
left: -24px;
|
||||
bottom: 0;
|
||||
border-top: 1px solid #1f232926;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 24px;
|
||||
|
||||
.danger-button {
|
||||
border: 1px solid var(--ed-color-danger);
|
||||
color: var(--ed-color-danger);
|
||||
border-radius: var(--ed-border-radius-base);
|
||||
min-width: 80px;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
margin: 0 16px;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.selected {
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
color: #646a73;
|
||||
margin-right: 12px;
|
||||
}
|
||||
}
|
||||
.tool-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 16px;
|
||||
|
||||
.page-title {
|
||||
font-weight: 500;
|
||||
font-size: 20px;
|
||||
line-height: 28px;
|
||||
}
|
||||
}
|
||||
.sqlbot-table {
|
||||
border-radius: 6px;
|
||||
width: 100%;
|
||||
max-height: calc(100vh - 200px);
|
||||
|
||||
&.is-filter {
|
||||
max-height: calc(100vh - 256px);
|
||||
}
|
||||
:deep(.ed-table) {
|
||||
--el-table-header-bg-color: #f5f7fa;
|
||||
--el-table-border-color: #ebeef5;
|
||||
--el-table-header-text-color: #606266;
|
||||
|
||||
th {
|
||||
font-weight: 600;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
td {
|
||||
height: 52px;
|
||||
}
|
||||
}
|
||||
.table-operate {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
.ed-icon + .ed-icon {
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.line {
|
||||
margin: 0 10px 0 12px;
|
||||
height: 16px;
|
||||
width: 1px;
|
||||
background-color: #1f232926;
|
||||
}
|
||||
|
||||
.ed-icon {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
color: #646a73;
|
||||
|
||||
&::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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
align-items: center;
|
||||
margin-top: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-status-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
|
||||
.ed-icon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,475 @@
|
||||
<script lang="ts" setup>
|
||||
import icon_warning_filled from '@/assets/svg/icon_info_colorful.svg'
|
||||
import icon_upload_outlined from '@/assets/svg/icon_upload_outlined.svg'
|
||||
import icon_fileExcel_colorful from '@/assets/datasource/icon_excel.png'
|
||||
import IconOpeDelete from '@/assets/svg/icon_delete.svg'
|
||||
import { ref, reactive, h } from 'vue'
|
||||
import {
|
||||
ElMessage,
|
||||
ElMessageBox,
|
||||
ElLoading,
|
||||
type UploadRequestOptions,
|
||||
type UploadProps,
|
||||
ElButton,
|
||||
} from 'element-plus-secondary'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { userImportApi } from '@/api/user'
|
||||
const { t } = useI18n()
|
||||
const defaultTip = t('user.xls_format_files')
|
||||
const loadingInstance = ref<any>(null)
|
||||
const dialogShow = ref(false)
|
||||
const form = ref({})
|
||||
const file = ref<any>(null)
|
||||
const fileName = ref('')
|
||||
const errorFileKey = ref(null)
|
||||
const emits = defineEmits(['refresh-grid'])
|
||||
const state = reactive({
|
||||
errList: [],
|
||||
filesTmp: [],
|
||||
})
|
||||
|
||||
const showLoading = () => {
|
||||
loadingInstance.value = ElLoading.service({ target: '.user-import-class' })
|
||||
}
|
||||
const closeLoading = () => {
|
||||
loadingInstance.value?.close()
|
||||
}
|
||||
const showDialog = () => {
|
||||
file.value = null
|
||||
fileName.value = ''
|
||||
errorFileKey.value = null
|
||||
dialogShow.value = true
|
||||
}
|
||||
const closeDialog = () => {
|
||||
dialogShow.value = false
|
||||
}
|
||||
const handleExceed: UploadProps['onExceed'] = () => {
|
||||
ElMessage.warning(t('userimport.exceedMsg'))
|
||||
}
|
||||
const handleError = () => {
|
||||
ElMessage.warning(t('user.contact_the_administrator'))
|
||||
}
|
||||
const uploadValidate = (file: any) => {
|
||||
const suffix = file.name.substring(file.name.lastIndexOf('.') + 1)
|
||||
if (suffix !== 'xlsx' && suffix !== 'xls') {
|
||||
ElMessage.warning(t('userimport.suffixMsg'))
|
||||
return false
|
||||
}
|
||||
|
||||
if (file.size / 1024 / 1024 > 10) {
|
||||
ElMessage.warning(t('userimport.limitMsg'))
|
||||
return false
|
||||
}
|
||||
state.errList = []
|
||||
return true
|
||||
}
|
||||
const fileSize = ref('-')
|
||||
const setFile = (options: UploadRequestOptions) => {
|
||||
file.value = options.file
|
||||
fileName.value = options.file.name
|
||||
fileSize.value = setSize(options.file.size)
|
||||
}
|
||||
|
||||
const buildFormData = (file: any, files: any, param: any) => {
|
||||
const formData = new FormData()
|
||||
if (file) {
|
||||
formData.append('file', file)
|
||||
}
|
||||
if (files) {
|
||||
files.forEach((f: any) => {
|
||||
formData.append('files', f)
|
||||
})
|
||||
}
|
||||
if (param) {
|
||||
formData.append('request', new Blob([JSON.stringify(param)], { type: 'application/json' }))
|
||||
}
|
||||
|
||||
return formData
|
||||
}
|
||||
const downExcel = () => {
|
||||
showLoading()
|
||||
userImportApi
|
||||
.downExcelTemplateApi()
|
||||
.then((res) => {
|
||||
const blobData = res.data
|
||||
const blob = new Blob([blobData], { type: 'application/vnd.ms-excel' })
|
||||
const link = document.createElement('a')
|
||||
link.style.display = 'none'
|
||||
link.href = URL.createObjectURL(blob)
|
||||
link.download = 'user.xlsx' // 下载的文件名
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
closeLoading()
|
||||
})
|
||||
.catch(() => {
|
||||
closeLoading()
|
||||
})
|
||||
}
|
||||
|
||||
const setSize = (size: any) => {
|
||||
let data = ''
|
||||
const _size = Number.parseFloat(size)
|
||||
if (_size < 1 * 1024) {
|
||||
//如果小于0.1KB转化成B
|
||||
data = _size.toFixed(2) + 'B'
|
||||
} else if (_size < 1 * 1024 * 1024) {
|
||||
//如果小于0.1MB转化成KB
|
||||
data = (_size / 1024).toFixed(2) + 'KB'
|
||||
} else if (_size < 1 * 1024 * 1024 * 1024) {
|
||||
//如果小于0.1GB转化成MB
|
||||
data = (_size / (1024 * 1024)).toFixed(2) + 'MB'
|
||||
} else {
|
||||
//其他转化成GB
|
||||
data = (_size / (1024 * 1024 * 1024)).toFixed(2) + 'GB'
|
||||
}
|
||||
const size_str = data + ''
|
||||
const len = size_str.indexOf('.')
|
||||
const dec = size_str.substr(len + 1, 2)
|
||||
if (dec == '00') {
|
||||
//当小数点后为00时 去掉小数部分
|
||||
return size_str.substring(0, len) + size_str.substr(len + 3, 2)
|
||||
}
|
||||
return size_str
|
||||
}
|
||||
|
||||
const toGrid = () => {
|
||||
file.value = null
|
||||
fileName.value = ''
|
||||
dialogShow.value = false
|
||||
emits('refresh-grid')
|
||||
}
|
||||
|
||||
const clearFile = () => {
|
||||
file.value = null
|
||||
fileName.value = ''
|
||||
}
|
||||
const sure = () => {
|
||||
const param = buildFormData(file.value, null, null)
|
||||
showLoading()
|
||||
userImportApi
|
||||
.importUserApi(param)
|
||||
.then((res) => {
|
||||
closeLoading()
|
||||
const data = res.data
|
||||
errorFileKey.value = data.dataKey
|
||||
closeDialog()
|
||||
showTips(data.successCount, data.errorCount)
|
||||
})
|
||||
.catch(() => {
|
||||
closeLoading()
|
||||
})
|
||||
}
|
||||
const downErrorExcel = () => {
|
||||
if (errorFileKey.value) {
|
||||
showLoading()
|
||||
userImportApi
|
||||
.downErrorRecordApi(errorFileKey.value)
|
||||
.then((res) => {
|
||||
const blobData = res.data
|
||||
const blob = new Blob([blobData], { type: 'application/vnd.ms-excel' })
|
||||
const link = document.createElement('a')
|
||||
link.style.display = 'none'
|
||||
link.href = URL.createObjectURL(blob)
|
||||
link.download = 'error.xlsx' // 下载的文件名
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
closeLoading()
|
||||
// closeDialog()
|
||||
})
|
||||
.catch(() => {
|
||||
closeLoading()
|
||||
})
|
||||
}
|
||||
}
|
||||
const showTips = (successCount: any, errorCount: any) => {
|
||||
let title = !errorCount
|
||||
? t('user.data_import_successful')
|
||||
: successCount
|
||||
? t('user.data_import_failed')
|
||||
: t('user.data_import_failed_de')
|
||||
const childrenDomList = [
|
||||
h('strong', null, title),
|
||||
h('br', {}, {}),
|
||||
h('span', null, t('user.imported_1_data', { msg: successCount })),
|
||||
]
|
||||
if (errorCount) {
|
||||
const errorCountDom = h('span', null, t('user.import_1_data', { msg: errorCount }))
|
||||
const errorDom = h('div', { class: 'error-record-tip flex-align-center' }, [
|
||||
h('span', null, t('user.can')),
|
||||
h(
|
||||
ElButton,
|
||||
{
|
||||
onClick: downErrorExcel,
|
||||
type: 'primary',
|
||||
text: true,
|
||||
class: 'down-button',
|
||||
},
|
||||
t('user.download_error_report')
|
||||
),
|
||||
h('span', null, t('user.modify_and_re_import')),
|
||||
])
|
||||
|
||||
childrenDomList.push(errorCountDom)
|
||||
childrenDomList.push(errorDom)
|
||||
}
|
||||
ElMessageBox.confirm('', {
|
||||
confirmButtonType: 'primary',
|
||||
type: !errorCount ? 'success' : successCount ? 'warning' : 'error',
|
||||
autofocus: false,
|
||||
dangerouslyUseHTMLString: true,
|
||||
message: h('div', { class: 'import-tip-box' }, childrenDomList),
|
||||
showClose: false,
|
||||
cancelButtonText: t('user.return_to_view'),
|
||||
confirmButtonText: t('user.continue_importing'),
|
||||
})
|
||||
.then(() => {
|
||||
clearErrorRecord()
|
||||
showDialog()
|
||||
emits('refresh-grid')
|
||||
})
|
||||
.catch(() => {
|
||||
clearErrorRecord()
|
||||
toGrid()
|
||||
})
|
||||
}
|
||||
const clearErrorRecord = () => {
|
||||
if (errorFileKey.value) {
|
||||
userImportApi.clearErrorApi(errorFileKey.value)
|
||||
}
|
||||
}
|
||||
|
||||
const rules = {
|
||||
file: [
|
||||
{
|
||||
required: true,
|
||||
message: t('datasource.please_enter') + t('common.empty') + t('user.file'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
showDialog,
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="dialogShow"
|
||||
:title="t('user.batch_import')"
|
||||
width="600px"
|
||||
model-class="user-import-class"
|
||||
@before-close="closeDialog"
|
||||
>
|
||||
<div class="down-template">
|
||||
<span class="icon-span">
|
||||
<el-icon>
|
||||
<Icon name="icon_warning_filled"><icon_warning_filled class="svg-icon" /></Icon>
|
||||
</el-icon>
|
||||
</span>
|
||||
<div class="down-template-content">
|
||||
<span>{{ t('user.please_first') }}</span>
|
||||
<el-button type="primary" text class="down-button" @click="downExcel">{{
|
||||
t('user.download_the_template')
|
||||
}}</el-button>
|
||||
<span>{{ t('user.required_and_upload') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<el-form
|
||||
ref="form"
|
||||
label-position="top"
|
||||
:rules="rules"
|
||||
class="form-content_error import-form"
|
||||
:model="form"
|
||||
label-width="0px"
|
||||
>
|
||||
<el-form-item prop="file" :label="t('user.file')" style="margin-bottom: 0px">
|
||||
<div v-if="fileName" class="pdf-card">
|
||||
<img :src="icon_fileExcel_colorful" width="40px" height="40px" />
|
||||
<div class="file-name">
|
||||
<div class="name">{{ fileName }}</div>
|
||||
<div class="size">{{ fileName.split('.')[1] }} - {{ fileSize }}</div>
|
||||
</div>
|
||||
<el-icon class="action-btn" size="16" @click="clearFile">
|
||||
<IconOpeDelete></IconOpeDelete>
|
||||
</el-icon>
|
||||
</div>
|
||||
<el-upload
|
||||
v-if="fileName"
|
||||
class="upload-user"
|
||||
action=""
|
||||
accept=".xlsx,.xls"
|
||||
:on-exceed="handleExceed"
|
||||
:before-upload="uploadValidate"
|
||||
:on-error="handleError"
|
||||
:show-file-list="false"
|
||||
:file-list="state.filesTmp"
|
||||
:http-request="setFile"
|
||||
>
|
||||
<el-button text style="line-height: 22px; height: 22px">
|
||||
{{ $t('user.change_file') }}
|
||||
</el-button>
|
||||
</el-upload>
|
||||
<el-upload
|
||||
v-else
|
||||
class="upload-user"
|
||||
action=""
|
||||
accept=".xlsx,.xls"
|
||||
:on-exceed="handleExceed"
|
||||
:before-upload="uploadValidate"
|
||||
:on-error="handleError"
|
||||
:show-file-list="false"
|
||||
:file-list="state.filesTmp"
|
||||
:http-request="setFile"
|
||||
>
|
||||
<el-button secondary>
|
||||
<el-icon size="16" style="margin-right: 4px">
|
||||
<icon_upload_outlined></icon_upload_outlined>
|
||||
</el-icon>
|
||||
{{ t('user.upload_file') }}</el-button
|
||||
>
|
||||
</el-upload>
|
||||
|
||||
<el-link
|
||||
v-if="!fileName"
|
||||
style="width: 100%; line-height: 22px; height: 22px; justify-content: start"
|
||||
class="font12"
|
||||
type="info"
|
||||
disabled
|
||||
>
|
||||
{{ defaultTip }}
|
||||
</el-link>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="closeDialog">{{ t('common.cancel') }}</el-button>
|
||||
<el-button
|
||||
:type="file && fileName ? 'primary' : 'info'"
|
||||
:disabled="!file || !fileName"
|
||||
@click="sure"
|
||||
>{{ t('user.import') }}</el-button
|
||||
>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<style lang="less">
|
||||
.upload-user {
|
||||
height: 32px;
|
||||
.ed-upload {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
.color-danger {
|
||||
:deep(.el-link--inner) {
|
||||
color: var(--deDanger, #f54a45) !important;
|
||||
}
|
||||
}
|
||||
.font12 {
|
||||
color: #8f959e !important;
|
||||
font-family: var(--de-custom_font, 'PingFang');
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
}
|
||||
.down-template {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
background: #d2f1e9;
|
||||
border-radius: 4px;
|
||||
padding-left: 10px;
|
||||
.icon-span {
|
||||
color: var(--ed-color-primary);
|
||||
font-size: 18px;
|
||||
i {
|
||||
top: 3px;
|
||||
}
|
||||
}
|
||||
.down-template-content {
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-left: 10px;
|
||||
.down-button {
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.import-form {
|
||||
margin-top: 16px;
|
||||
|
||||
.pdf-card {
|
||||
width: 100%;
|
||||
height: 58px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 16px 0 12px;
|
||||
border: 1px solid #dee0e3;
|
||||
border-radius: 6px;
|
||||
|
||||
.file-name {
|
||||
margin-left: 8px;
|
||||
.name {
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.size {
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
color: #8f959e;
|
||||
}
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.ed-icon {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
color: #646a73;
|
||||
|
||||
&::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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.import-tip-box {
|
||||
strong {
|
||||
font-size: 16px;
|
||||
}
|
||||
span {
|
||||
font-size: 13px;
|
||||
}
|
||||
.error-record-tip {
|
||||
font-size: 13px;
|
||||
flex-flow: wrap;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,212 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="centerDialogVisible"
|
||||
:title="$t('workspace.add_member')"
|
||||
modal-class="authorized-workspace"
|
||||
width="840"
|
||||
>
|
||||
<p class="mb-8 lighter">{{ $t('workspace.member_type') }}</p>
|
||||
<el-radio-group v-model="listType">
|
||||
<el-radio value="0">{{ $t('workspace.ordinary_member') }}</el-radio>
|
||||
<el-radio value="1">{{ $t('workspace.administrator') }}</el-radio>
|
||||
</el-radio-group>
|
||||
<p class="mb-8 lighter mt-16">{{ $t('workspace.select_member') }}</p>
|
||||
<div v-loading="loading" class="flex border" style="height: 428px; border-radius: 6px">
|
||||
<div class="p-16 border-r">
|
||||
<el-input
|
||||
v-model="search"
|
||||
:validate-event="false"
|
||||
:placeholder="$t('datasource.search')"
|
||||
style="width: 364px"
|
||||
clearable
|
||||
>
|
||||
<template #prefix>
|
||||
<el-icon>
|
||||
<Search></Search>
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
<div class="mt-8">
|
||||
<el-checkbox
|
||||
v-if="!search"
|
||||
v-model="checkAll"
|
||||
class="mb-8"
|
||||
:indeterminate="isIndeterminate"
|
||||
@change="handleCheckAllChange"
|
||||
>
|
||||
{{ $t('datasource.select_all') }}
|
||||
</el-checkbox>
|
||||
<el-checkbox-group
|
||||
v-model="checkedWorkspace"
|
||||
class="checkbox-group-block"
|
||||
@change="handleCheckedWorkspaceChange"
|
||||
>
|
||||
<el-checkbox
|
||||
v-for="space in workspaceWithKeywords"
|
||||
:key="space.id"
|
||||
:label="space.name"
|
||||
:value="space"
|
||||
>
|
||||
<div class="flex">
|
||||
<img :src="avatar_personal" width="28px" height="28px" />
|
||||
<span class="ml-4"> {{ space.name }}</span>
|
||||
</div>
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-16 w-full">
|
||||
<div class="flex-between mb-16">
|
||||
<span class="lighter">
|
||||
{{ $t('workspace.selected_2_people', { msg: checkedWorkspace.length }) }}
|
||||
</span>
|
||||
|
||||
<el-button text @click="clearWorkspaceAll">
|
||||
{{ $t('workspace.clear') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<div v-for="ele in checkedWorkspace" :key="ele.name" class="flex-between">
|
||||
<div class="flex align-center">
|
||||
<img :src="avatar_personal" width="28px" height="28px" />
|
||||
<span class="ml-4 lighter">{{ ele.name }}</span>
|
||||
</div>
|
||||
<el-button text>
|
||||
<el-icon :size="18" @click="clearWorkspace(ele)"><Close /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="centerDialogVisible = false"> {{ $t('common.cancel') }}</el-button>
|
||||
<el-button v-if="!checkedWorkspace.length" disabled type="info">{{
|
||||
$t('model.add')
|
||||
}}</el-button>
|
||||
<el-button v-else type="primary" @click="handleConfirm"> {{ $t('model.add') }} </el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
import avatar_personal from '@/assets/workspace/avatar_personal.png'
|
||||
import Close from '@/assets/svg/icon_close_outlined.svg'
|
||||
import Search from '@/assets/svg/icon_search-outline_outlined.svg'
|
||||
import type { CheckboxValueType } from 'element-plus-secondary'
|
||||
const checkAll = ref(false)
|
||||
const isIndeterminate = ref(true)
|
||||
const checkedWorkspace = ref<any[]>([])
|
||||
const workspace = ref<any[]>([])
|
||||
const listType = ref('0')
|
||||
const search = ref('')
|
||||
const loading = ref(false)
|
||||
const centerDialogVisible = ref(false)
|
||||
|
||||
const workspaceWithKeywords = computed(() => {
|
||||
return workspace.value.filter((ele: any) => (ele.name as string).includes(search.value))
|
||||
})
|
||||
const handleCheckAllChange = (val: CheckboxValueType) => {
|
||||
checkedWorkspace.value = val ? workspace.value : []
|
||||
isIndeterminate.value = false
|
||||
if (!val) {
|
||||
clearWorkspaceAll()
|
||||
}
|
||||
}
|
||||
const handleCheckedWorkspaceChange = (value: CheckboxValueType[]) => {
|
||||
const checkedCount = value.length
|
||||
checkAll.value = checkedCount === workspace.value.length
|
||||
isIndeterminate.value = checkedCount > 0 && checkedCount < workspace.value.length
|
||||
}
|
||||
|
||||
const open = async () => {
|
||||
loading.value = true
|
||||
// const [authList, systemWorkspaceList] = await Promise.all([
|
||||
// authorizationApi[`getSharedAuthorization${type}`](id),
|
||||
// workspaceApi.getSystemWorkspaceList(),
|
||||
// ])
|
||||
// workspace.value = systemWorkspaceList.data as any
|
||||
// listType.value = (authList.data || {}).authentication_type || 'WHITE_LIST'
|
||||
// let workspace_id_list = (authList.data || {}).workspace_id_list || []
|
||||
// checkedWorkspace.value = workspace.value.filter((ele) => workspace_id_list.includes(ele.id))
|
||||
// handleCheckedWorkspaceChange(checkedWorkspace.value)
|
||||
// loading.value = false
|
||||
// centerDialogVisible.value = true
|
||||
}
|
||||
|
||||
const handleConfirm = () => {
|
||||
// authorizationApi[`postSharedAuthorization${currentType}`](knowledge_id, {
|
||||
// workspace_id_list: checkedWorkspace.value.map((ele: any) => ele.id),
|
||||
// authentication_type: listType.value,
|
||||
// }).then(() => {
|
||||
// centerDialogVisible.value = false
|
||||
// })
|
||||
}
|
||||
|
||||
const clearWorkspace = (val: any) => {
|
||||
checkedWorkspace.value = checkedWorkspace.value.filter((ele: any) => ele.id !== val.id)
|
||||
}
|
||||
|
||||
const clearWorkspaceAll = () => {
|
||||
checkedWorkspace.value = []
|
||||
handleCheckedWorkspaceChange([])
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
open,
|
||||
})
|
||||
</script>
|
||||
<style lang="less">
|
||||
.authorized-workspace {
|
||||
.mb-8 {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.mt-16 {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.p-16 {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.lighter {
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.border {
|
||||
border: 1px solid #dee0e3;
|
||||
}
|
||||
|
||||
.w-full {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.mt-8 {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.align-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.flex-between {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.ml-4 {
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.border-r {
|
||||
border-right: 1px solid #dee0e3;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,108 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { t } = useI18n()
|
||||
const paramsRef = ref()
|
||||
const paramsForm = reactive({
|
||||
name: '',
|
||||
key: '',
|
||||
val: '',
|
||||
})
|
||||
|
||||
const rules = {
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
message: t('datasource.please_enter') + t('common.empty') + t('model.display_name'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
key: [
|
||||
{
|
||||
required: true,
|
||||
message: t('datasource.please_enter') + t('common.empty') + t('model.parameters'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
val: [
|
||||
{
|
||||
required: true,
|
||||
message: t('datasource.please_enter') + t('common.empty') + t('model.parameter_value'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const initForm = (item: any) => {
|
||||
if (item) {
|
||||
Object.assign(paramsForm, { ...item })
|
||||
}
|
||||
paramsRef.value.clearValidate()
|
||||
}
|
||||
|
||||
const emits = defineEmits(['submit'])
|
||||
|
||||
const submit = () => {
|
||||
paramsRef.value.validate((res: any) => {
|
||||
if (res) {
|
||||
emits('submit', paramsForm)
|
||||
}
|
||||
})
|
||||
}
|
||||
defineExpose({
|
||||
initForm,
|
||||
submit,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="params-form">
|
||||
<el-form
|
||||
ref="paramsRef"
|
||||
:rules="rules"
|
||||
label-position="top"
|
||||
:model="paramsForm"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-form-item prop="key" :label="$t('model.parameters')">
|
||||
<el-input
|
||||
v-model="paramsForm.key"
|
||||
:placeholder="$t('datasource.please_enter') + $t('common.empty') + $t('model.parameters')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item prop="name" :label="$t('model.display_name')">
|
||||
<el-input
|
||||
v-model="paramsForm.name"
|
||||
:placeholder="
|
||||
$t('datasource.please_enter') + $t('common.empty') + $t('model.display_name')
|
||||
"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item prop="val" :label="$t('model.parameter_value')">
|
||||
<el-input
|
||||
v-model="paramsForm.val"
|
||||
:placeholder="
|
||||
$t('datasource.please_enter') + $t('common.empty') + $t('model.parameter_value')
|
||||
"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.params-form {
|
||||
.ed-form-item--default {
|
||||
margin-bottom: 16px;
|
||||
|
||||
&.is-error {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.ed-input-number {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,846 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted, reactive } from 'vue'
|
||||
import { workspaceList, workspaceUserList } from '@/api/workspace'
|
||||
import icon_add_outlined from '@/assets/svg/icon_add_outlined.svg'
|
||||
import arrow_down from '@/assets/svg/arrow-down.svg'
|
||||
import icon_searchOutline_outlined from '@/assets/svg/icon_search-outline_outlined.svg'
|
||||
import EmptyBackground from '@/views/dashboard/common/EmptyBackground.vue'
|
||||
import icon_form_outlined from '@/assets/svg/icon_moments-categories_outlined.svg'
|
||||
import assigned from '@/assets/svg/icon_assigned_outlined.svg'
|
||||
import AuthorizedWorkspaceDialog from './AuthorizedWorkspaceDialog.vue'
|
||||
import rename from '@/assets/svg/icon_rename_outlined.svg'
|
||||
import icon_member from '@/assets/svg/icon_member.svg'
|
||||
import SuccessFilled from '@/assets/svg/gou_icon.svg'
|
||||
import CircleCloseFilled from '@/assets/svg/icon_ban_filled.svg'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import delIcon from '@/assets/svg/icon_delete.svg'
|
||||
import icon_more_outlined from '@/assets/svg/icon_more_outlined.svg'
|
||||
import icon_done_outlined from '@/assets/svg/icon_done_outlined.svg'
|
||||
|
||||
const { t } = useI18n()
|
||||
const multipleSelectionAll = ref<any[]>([])
|
||||
const tableList = ref([] as any[])
|
||||
const keywords = ref('')
|
||||
const keywordsMember = ref('')
|
||||
const workspaceFormRef = ref()
|
||||
const authorizedWorkspaceDialog = ref()
|
||||
const tableListWithSearch = computed(() => {
|
||||
if (!keywords.value) return tableList.value
|
||||
return tableList.value.filter((ele) =>
|
||||
ele.name.toLowerCase().includes(keywords.value.toLowerCase())
|
||||
)
|
||||
})
|
||||
const workspaceForm = reactive({
|
||||
name: '',
|
||||
id: '',
|
||||
})
|
||||
|
||||
const userTypeList = [
|
||||
{
|
||||
name: t('workspace.administrator'),
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
name: t('workspace.ordinary_member'),
|
||||
value: '1',
|
||||
},
|
||||
]
|
||||
|
||||
const handleUserTypeChange = (val: any, row: any) => {
|
||||
row.user_source = val
|
||||
}
|
||||
|
||||
const rules = {
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
message: t('datasource.please_enter') + t('common.empty') + t('workspace.workspace_name'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
}
|
||||
const fieldListWithSearch = computed(() => {
|
||||
if (!keywords.value) return fieldList.value
|
||||
return fieldList.value.filter((ele: any) =>
|
||||
ele.name.toLowerCase().includes(keywordsMember.value.toLowerCase())
|
||||
)
|
||||
})
|
||||
const currentTable = ref<any>({})
|
||||
const init = () => {
|
||||
workspaceList().then((res) => {
|
||||
tableList.value = res
|
||||
})
|
||||
}
|
||||
onMounted(() => {
|
||||
init()
|
||||
})
|
||||
const fieldDialog = ref<boolean>(false)
|
||||
const multipleTableRef = ref()
|
||||
const isIndeterminate = ref(true)
|
||||
const checkAll = ref(false)
|
||||
const fieldList = ref<any>([])
|
||||
const pageInfo = reactive({
|
||||
currentPage: 1,
|
||||
pageSize: 1,
|
||||
total: 0,
|
||||
})
|
||||
|
||||
const clickTable = (table: any) => {
|
||||
currentTable.value = table
|
||||
pageInfo.currentPage = 1
|
||||
search()
|
||||
}
|
||||
const cancelDelete = () => {
|
||||
multipleSelectionAll.value = []
|
||||
checkAll.value = false
|
||||
isIndeterminate.value = false
|
||||
}
|
||||
const deleteBatchUser = () => {
|
||||
ElMessageBox.confirm(
|
||||
t('workspace.selected_2_members', { msg: multipleSelectionAll.value.length }),
|
||||
{
|
||||
confirmButtonType: 'danger',
|
||||
confirmButtonText: t('dashboard.delete'),
|
||||
cancelButtonText: t('common.cancel'),
|
||||
customClass: 'confirm-no_icon',
|
||||
autofocus: false,
|
||||
callback: (val: string) => {
|
||||
console.log(val)
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
const deleteHandler = (row: any) => {
|
||||
ElMessageBox.confirm(t('workspace.member_feng_yibudao', { msg: row.name }), {
|
||||
confirmButtonType: 'danger',
|
||||
confirmButtonText: t('dashboard.delete'),
|
||||
cancelButtonText: t('common.cancel'),
|
||||
customClass: 'confirm-no_icon',
|
||||
autofocus: false,
|
||||
callback: (val: string) => {
|
||||
console.log(val)
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
/* userApi.delete(id).then(() => {
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: 'Delete completed',
|
||||
})
|
||||
search()
|
||||
}) */
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage({
|
||||
type: 'info',
|
||||
message: 'Delete canceled',
|
||||
})
|
||||
})
|
||||
}
|
||||
const handleSelectionChange = (val: any[]) => {
|
||||
const ids = fieldList.value.map((ele: any) => ele.id)
|
||||
multipleSelectionAll.value = [
|
||||
...multipleSelectionAll.value.filter((ele) => !ids.includes(ele.id)),
|
||||
...val,
|
||||
]
|
||||
isIndeterminate.value = !(val.length === 0 || val.length === fieldList.value.length)
|
||||
checkAll.value = val.length === fieldList.value.length
|
||||
}
|
||||
const handleCheckAllChange = (val: any) => {
|
||||
isIndeterminate.value = false
|
||||
handleSelectionChange(val ? fieldList.value : [])
|
||||
if (val) {
|
||||
handleToggleRowSelection()
|
||||
} else {
|
||||
multipleTableRef.value.clearSelection()
|
||||
}
|
||||
}
|
||||
|
||||
const handleToggleRowSelection = (check: boolean = true) => {
|
||||
let i = 0
|
||||
const ids = multipleSelectionAll.value.map((ele: any) => ele.id)
|
||||
for (const key in fieldList.value) {
|
||||
if (ids.includes((fieldList.value[key] as any).id)) {
|
||||
i += 1
|
||||
multipleTableRef.value.toggleRowSelection(fieldList.value[key], check)
|
||||
}
|
||||
}
|
||||
checkAll.value = i === fieldList.value.length
|
||||
isIndeterminate.value = !(i === 0 || i === fieldList.value.length)
|
||||
}
|
||||
|
||||
const handleAddMember = () => {
|
||||
authorizedWorkspaceDialog.value.open(1)
|
||||
}
|
||||
const search = () => {
|
||||
workspaceUserList({ oid: currentTable.value.id }, pageInfo.currentPage, pageInfo.pageSize).then(
|
||||
(res) => {
|
||||
fieldList.value = res.items
|
||||
fieldList.value = [{ id: 0, user_source: '管理员' }]
|
||||
// datasourceApi.previewData(props.info.id, buildData()).then((res) => {
|
||||
// previewData.value = res
|
||||
// })
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const closeField = () => {
|
||||
fieldDialog.value = false
|
||||
}
|
||||
|
||||
const saveField = () => {
|
||||
workspaceFormRef.value.validate((res: any) => {
|
||||
if (res) {
|
||||
console.log(res)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const delWorkspace = (row: any) => {
|
||||
ElMessageBox.confirm(t('workspace.workspace_de_workspace', { msg: row.name }), {
|
||||
confirmButtonType: 'danger',
|
||||
confirmButtonText: t('dashboard.delete'),
|
||||
cancelButtonText: t('common.cancel'),
|
||||
customClass: 'confirm-no_icon',
|
||||
autofocus: false,
|
||||
callback: (val: string) => {
|
||||
console.log(val)
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
/* userApi.delete(id).then(() => {
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: 'Delete completed',
|
||||
})
|
||||
search()
|
||||
}) */
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage({
|
||||
type: 'info',
|
||||
message: 'Delete canceled',
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const addWorkspace = (row: any) => {
|
||||
if (row) {
|
||||
workspaceForm.name = row.name
|
||||
workspaceForm.id = row.id
|
||||
} else {
|
||||
workspaceForm.name = ''
|
||||
workspaceForm.id = ''
|
||||
}
|
||||
fieldDialog.value = true
|
||||
}
|
||||
|
||||
const handleSizeChange = (val: number) => {
|
||||
pageInfo.pageSize = val
|
||||
search()
|
||||
}
|
||||
const handleCurrentChange = (val: number) => {
|
||||
pageInfo.currentPage = val
|
||||
search()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="workspace no-padding">
|
||||
<div class="side-list">
|
||||
<div class="select-table_top">
|
||||
{{ $t('user.workspace') }}
|
||||
|
||||
<el-tooltip effect="dark" :content="$t('workspace.add_workspace')" placement="top">
|
||||
<el-icon size="18" @click="addWorkspace">
|
||||
<icon_add_outlined></icon_add_outlined>
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<el-input
|
||||
v-model="keywords"
|
||||
clearable
|
||||
style="width: 232px"
|
||||
:placeholder="$t('datasource.search')"
|
||||
>
|
||||
<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.name"
|
||||
class="model"
|
||||
:class="currentTable.name === ele.name && 'isActive'"
|
||||
:title="ele.name"
|
||||
@click="clickTable(ele)"
|
||||
>
|
||||
<el-icon size="16">
|
||||
<icon_form_outlined></icon_form_outlined>
|
||||
</el-icon>
|
||||
<span class="name">{{ ele.name }}</span>
|
||||
|
||||
<el-popover
|
||||
trigger="click"
|
||||
:teleported="false"
|
||||
popper-class="popover-card"
|
||||
placement="bottom"
|
||||
>
|
||||
<template #reference>
|
||||
<el-icon class="more" size="16" style="margin-left: auto" @click.stop>
|
||||
<icon_more_outlined></icon_more_outlined>
|
||||
</el-icon>
|
||||
</template>
|
||||
<div class="content">
|
||||
<div class="item" @click.stop="addWorkspace(ele)">
|
||||
<el-icon size="16">
|
||||
<rename></rename>
|
||||
</el-icon>
|
||||
{{ $t('datasource.edit') }}
|
||||
</div>
|
||||
<div class="item" @click.stop="delWorkspace(ele)">
|
||||
<el-icon size="16">
|
||||
<delIcon></delIcon>
|
||||
</el-icon>
|
||||
{{ $t('dashboard.delete') }}
|
||||
</div>
|
||||
</div>
|
||||
</el-popover>
|
||||
</div>
|
||||
<div v-if="!!keywords && !tableListWithSearch.length" class="no-result">
|
||||
{{ $t('workspace.historical_dialogue') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="currentTable.name" class="info-table">
|
||||
<div class="table-name">
|
||||
<div class="name">
|
||||
{{ currentTable.name }}
|
||||
<span class="line"></span>
|
||||
<el-icon style="margin: 0 4px 0 8px" size="16">
|
||||
<icon_member></icon_member>
|
||||
</el-icon>
|
||||
{{ pageInfo.total }}
|
||||
</div>
|
||||
<div class="notes">
|
||||
<el-button type="primary" @click="handleAddMember">
|
||||
<template #icon>
|
||||
<icon_add_outlined></icon_add_outlined>
|
||||
</template>
|
||||
{{ $t('workspace.add_member') }}
|
||||
</el-button>
|
||||
<el-input
|
||||
v-model="keywordsMember"
|
||||
clearable
|
||||
style="width: 232px"
|
||||
:placeholder="$t('workspace.name_username_email')"
|
||||
>
|
||||
<template #prefix>
|
||||
<el-icon>
|
||||
<icon_searchOutline_outlined class="svg-icon" />
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-content">
|
||||
<div class="preview-or-schema">
|
||||
<el-table
|
||||
ref="multipleTableRef"
|
||||
:data="fieldListWithSearch"
|
||||
style="width: 100%"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column prop="name" :label="$t('user.name')" width="160" />
|
||||
<el-table-column prop="account" :label="$t('user.account')" width="160" />
|
||||
<el-table-column prop="status" :label="$t('user.user_status')">
|
||||
<template #default="scope">
|
||||
<div
|
||||
class="user-status-container"
|
||||
:class="[scope.row.status ? 'active' : 'disabled']"
|
||||
>
|
||||
<el-icon size="16">
|
||||
<SuccessFilled v-if="scope.row.status" />
|
||||
<CircleCloseFilled v-else />
|
||||
</el-icon>
|
||||
<span>{{ $t(`user.${scope.row.status ? 'enabled' : 'disabled'}`) }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="email" :label="$t('user.email')" width="204" />
|
||||
<el-table-column
|
||||
class-name="user-source"
|
||||
prop="user_source"
|
||||
:label="$t('user.user_source')"
|
||||
width="120"
|
||||
>
|
||||
<template #default="scope">
|
||||
<el-popover popper-class="system-workspace_user" placement="bottom">
|
||||
<template #reference>
|
||||
<div style="display: flex; align-items: center; justify-content: space-between">
|
||||
<span>{{ scope.row.user_source }}</span>
|
||||
<el-icon size="16">
|
||||
<arrow_down></arrow_down>
|
||||
</el-icon>
|
||||
</div>
|
||||
</template>
|
||||
<div class="popover">
|
||||
<div class="popover-content">
|
||||
<div
|
||||
v-for="ele in userTypeList"
|
||||
:key="ele.name"
|
||||
class="popover-item"
|
||||
:class="ele.name === scope.row.user_source && 'isActive'"
|
||||
@click="handleUserTypeChange(ele.value, scope.row)"
|
||||
>
|
||||
<div class="model-name">{{ ele.name }}</div>
|
||||
<el-icon size="16" class="done">
|
||||
<icon_done_outlined></icon_done_outlined>
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-popover>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column fixed="right" width="80" :label="t('ds.actions')">
|
||||
<template #default="scope">
|
||||
<div class="field-comment">
|
||||
<el-tooltip
|
||||
:offset="14"
|
||||
effect="dark"
|
||||
:content="$t('workspace.remove')"
|
||||
placement="top"
|
||||
>
|
||||
<el-icon class="action-btn" size="16" @click="deleteHandler(scope.row)">
|
||||
<assigned></assigned>
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template #empty>
|
||||
<EmptyBackground
|
||||
v-if="!keywordsMember && !fieldListWithSearch.length"
|
||||
:description="$t('workspace.no_user')"
|
||||
img-type="noneWhite"
|
||||
/>
|
||||
|
||||
<EmptyBackground
|
||||
v-if="!!keywordsMember && !fieldListWithSearch.length"
|
||||
:description="$t('datasource.relevant_content_found')"
|
||||
img-type="tree"
|
||||
/>
|
||||
</template>
|
||||
</el-table>
|
||||
</div>
|
||||
<div class="pagination-container">
|
||||
<el-pagination
|
||||
v-model:current-page="pageInfo.currentPage"
|
||||
v-model:page-size="pageInfo.pageSize"
|
||||
:page-sizes="[10, 20, 30]"
|
||||
:background="true"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="pageInfo.total"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="multipleSelectionAll.length" class="bottom-select">
|
||||
<el-checkbox
|
||||
v-model="checkAll"
|
||||
:indeterminate="isIndeterminate"
|
||||
@change="handleCheckAllChange"
|
||||
>
|
||||
{{ $t('datasource.select_all') }}
|
||||
</el-checkbox>
|
||||
|
||||
<button class="danger-button" @click="deleteBatchUser">{{ $t('workspace.remove') }}</button>
|
||||
|
||||
<span class="selected">{{
|
||||
$t('user.selected_2_items', { msg: multipleSelectionAll.length })
|
||||
}}</span>
|
||||
|
||||
<el-button text @click="cancelDelete">
|
||||
{{ $t('common.cancel') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<AuthorizedWorkspaceDialog ref="authorizedWorkspaceDialog"></AuthorizedWorkspaceDialog>
|
||||
<el-dialog
|
||||
v-model="fieldDialog"
|
||||
:title="$t(workspaceForm.id ? 'workspace.rename_a_workspace' : 'workspace.add_workspace')"
|
||||
width="420"
|
||||
destroy-on-close
|
||||
:close-on-click-modal="false"
|
||||
@before-closed="closeField"
|
||||
>
|
||||
<el-form
|
||||
ref="workspaceFormRef"
|
||||
:model="workspaceForm"
|
||||
label-width="180px"
|
||||
label-position="top"
|
||||
:rules="rules"
|
||||
class="form-content_error"
|
||||
>
|
||||
<el-form-item prop="name" :label="t('workspace.workspace_name')">
|
||||
<el-input
|
||||
v-model="workspaceForm.name"
|
||||
:placeholder="
|
||||
$t('datasource.please_enter') + $t('common.empty') + $t('workspace.workspace_name')
|
||||
"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<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(workspaceForm.id ? 'common.save' : 'model.add')
|
||||
}}</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.workspace {
|
||||
height: 100%;
|
||||
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 {
|
||||
cursor: pointer;
|
||||
color: var(--ed-color-primary);
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
&::after {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
display: none;
|
||||
position: absolute;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
background: #1cba901a;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
border-radius: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ed-input {
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
.list-content {
|
||||
height: calc(100% - 100px);
|
||||
|
||||
.more {
|
||||
display: none;
|
||||
&.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.no-result {
|
||||
margin-top: 72px;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
text-align: center;
|
||||
color: #646a73;
|
||||
}
|
||||
.model {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 8px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
padding-right: 8px;
|
||||
|
||||
.name {
|
||||
margin-left: 8px;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
max-width: 80%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
&:hover {
|
||||
background: #1f23291a;
|
||||
.more {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
&.isActive {
|
||||
background: #1cba901a;
|
||||
color: var(--ed-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.info-table {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: calc(100% - 280px);
|
||||
height: 100%;
|
||||
.table-name {
|
||||
padding: 16px 24px 0 24px;
|
||||
|
||||
.name {
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.line {
|
||||
height: 16px;
|
||||
width: 1px;
|
||||
background: #bbbfc4;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.notes {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin: 16px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.table-content {
|
||||
padding: 0 24px 16px 24px;
|
||||
height: calc(100% - 104px);
|
||||
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
align-items: center;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.preview-or-schema {
|
||||
:deep(.user-source) {
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
.ed-icon {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
.user-status-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
|
||||
.ed-icon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.preview-num {
|
||||
margin: 12px 0;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
color: #646a73;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.system-workspace_user.system-workspace_user {
|
||||
padding: 0;
|
||||
width: 120px !important;
|
||||
min-width: 120px !important;
|
||||
box-shadow: 0px 4px 8px 0px #1f23291a;
|
||||
border: 1px solid #dee0e3;
|
||||
|
||||
.popover {
|
||||
.popover-content {
|
||||
padding: 4px;
|
||||
position: relative;
|
||||
&::after {
|
||||
position: absolute;
|
||||
content: '';
|
||||
left: 0;
|
||||
top: 40px;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background: #1f232926;
|
||||
}
|
||||
}
|
||||
.popover-item {
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 12px;
|
||||
padding-right: 8px;
|
||||
position: relative;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background: #1f23291a;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
margin: 9px 0 0 0;
|
||||
}
|
||||
|
||||
.model-name {
|
||||
margin-left: 8px;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.done {
|
||||
margin-left: auto;
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.isActive {
|
||||
color: var(--ed-color-primary);
|
||||
|
||||
.done {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||