mirror of
https://gh-proxy.org/https://github.com/klinsmaya/SQLBot.git
synced 2026-11-08 01:06:59 +08:00
feat(Data Source): SQLBot supports custom data types in data sources #768
This commit is contained in:
@@ -6,6 +6,7 @@ export const datasourceApi = {
|
||||
relationGet: (id: any) => request.post(`/table_relation/get/${id}`),
|
||||
relationSave: (dsId: any, data: any) => request.post(`/table_relation/save/${dsId}`, data),
|
||||
add: (data: any) => request.post('/datasource/add', data),
|
||||
importToDb: (data: any) => request.post('/datasource/importToDb', data),
|
||||
list: () => request.get('/datasource/list'),
|
||||
update: (data: any) => request.post('/datasource/update', data),
|
||||
delete: (id: number, name: string) => request.post(`/datasource/delete/${id}/${name}`),
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.64869 6.28798L8.09994 10.6605C8.24844 10.7797 8.48944 10.7797 8.63818 10.6605C8.70969 10.6032 8.74969 10.5255 8.74969 10.4447V1.55496C8.74994 1.38672 8.57944 1.24997 8.36919 1.24997C8.26819 1.24997 8.17169 1.28222 8.10019 1.33947L2.64869 5.71197C2.45044 5.87097 2.45044 6.12897 2.64869 6.28797V6.28798Z" fill="#8F959E"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 436 B |
@@ -1,3 +1,3 @@
|
||||
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.79272 5.24009L3.25001 8.88384C3.12626 8.98321 2.92543 8.98321 2.80148 8.88384C2.74189 8.83613 2.70856 8.77134 2.70856 8.70405V1.29591C2.70834 1.1557 2.85043 1.04175 3.02564 1.04175C3.10981 1.04175 3.19022 1.06862 3.24981 1.11633L7.79272 4.76008C7.95793 4.89258 7.95793 5.10758 7.79272 5.24008V5.24009Z" fill="#646A73"/>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.35148 6.28798L3.90023 10.6605C3.75173 10.7797 3.51074 10.7797 3.36199 10.6605C3.29048 10.6032 3.25049 10.5255 3.25049 10.4447V1.55496C3.25023 1.38672 3.42074 1.24997 3.63098 1.24997C3.73199 1.24997 3.82848 1.28222 3.89999 1.33947L9.35148 5.71197C9.54974 5.87097 9.54974 6.12897 9.35148 6.28797V6.28798Z" fill="#8F959E"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 435 B After Width: | Height: | Size: 436 B |
@@ -33,6 +33,9 @@
|
||||
"enter_variable_value": "Please Enter Variable Value"
|
||||
},
|
||||
"sync": {
|
||||
"records":"Displaying {num} records out of {total}",
|
||||
"confirm_upload":"Confirm Upload",
|
||||
"field_details":"Field Details",
|
||||
"integration": "Platform integration needs to be enabled.",
|
||||
"the_existing_user": "If the user already exists, overwrite the existing user.",
|
||||
"sync_users": "Sync Users",
|
||||
@@ -442,6 +445,9 @@
|
||||
"get_schema": "Get Schema"
|
||||
},
|
||||
"model": {
|
||||
"int": "Integer",
|
||||
"float": "Float",
|
||||
"string": "String",
|
||||
"default_model": "Default model",
|
||||
"model_type": "Model type",
|
||||
"basic_model": "Basic model",
|
||||
|
||||
@@ -7,22 +7,6 @@
|
||||
"AI Model Configuration": "모델 구성",
|
||||
"Details": "세부"
|
||||
},
|
||||
"sync": {
|
||||
"integration": "플랫폼 통합을 활성화해야 합니다.",
|
||||
"the_existing_user": "해당 사용자가 이미 존재하는 경우 기존 사용자를 덮어씁니다.",
|
||||
"sync_users": "사용자 동기화",
|
||||
"sync_wechat_users": "위챗 사용자 동기화",
|
||||
"sync_dingtalk_users": "딩톡 사용자 동기화",
|
||||
"sync_lark_users": "라크 사용자 동기화",
|
||||
"sync_complete": "동기화 완료",
|
||||
"synced_10_users": "{num}명 동기화 성공",
|
||||
"failed_3_users": "{failed}명 동기화 성공, {success}명 동기화 실패",
|
||||
"download_failure_list": "실패 목록 다운로드",
|
||||
"sync_failed": "동기화 실패",
|
||||
"failed_10_users": "{failed}명 동기화 실패",
|
||||
"continue_syncing": "동기화 계속",
|
||||
"return_to_view": "화면으로 돌아가기"
|
||||
},
|
||||
"variables": {
|
||||
"please_select_date": "날짜를 선택하십시오",
|
||||
"number_variable_error": "올바른 숫자 범위를 입력하십시오.",
|
||||
@@ -48,6 +32,25 @@
|
||||
"enter_variable_name": "변수 이름을 입력하세요",
|
||||
"enter_variable_value": "변수 값을 입력하세요"
|
||||
},
|
||||
"sync": {
|
||||
"records": "{total}개 중 {num}개의 레코드 표시",
|
||||
"confirm_upload": "업로드 확인",
|
||||
"field_details": "필드 세부 정보",
|
||||
"integration": "플랫폼 통합을 활성화해야 합니다.",
|
||||
"the_existing_user": "해당 사용자가 이미 존재하는 경우 기존 사용자를 덮어씁니다.",
|
||||
"sync_users": "사용자 동기화",
|
||||
"sync_wechat_users": "위챗 사용자 동기화",
|
||||
"sync_dingtalk_users": "딩톡 사용자 동기화",
|
||||
"sync_lark_users": "라크 사용자 동기화",
|
||||
"sync_complete": "동기화 완료",
|
||||
"synced_10_users": "{num}명 동기화 성공",
|
||||
"failed_3_users": "{failed}명 동기화 성공, {success}명 동기화 실패",
|
||||
"download_failure_list": "실패 목록 다운로드",
|
||||
"sync_failed": "동기화 실패",
|
||||
"failed_10_users": "{failed}명 동기화 실패",
|
||||
"continue_syncing": "동기화 계속",
|
||||
"return_to_view": "화면으로 돌아가기"
|
||||
},
|
||||
"parameter": {
|
||||
"execution_details": "실행 세부 정보",
|
||||
"overview": "개요",
|
||||
@@ -442,6 +445,9 @@
|
||||
"get_schema": "스키마 가져오기"
|
||||
},
|
||||
"model": {
|
||||
"int": "정수",
|
||||
"float": "소수",
|
||||
"string": "문자열",
|
||||
"default_model": "기본 모델",
|
||||
"model_type": "모델 유형",
|
||||
"basic_model": "기본 모델",
|
||||
@@ -948,4 +954,4 @@
|
||||
"to_doc": "API 보기",
|
||||
"trigger_limit": "최대 {0}개의 API 키 생성 지원"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,22 +7,6 @@
|
||||
"AI Model Configuration": "模型配置",
|
||||
"Details": "详情"
|
||||
},
|
||||
"sync": {
|
||||
"integration": "需开启平台对接",
|
||||
"the_existing_user": "若用户已存在,覆盖旧用户",
|
||||
"sync_users": "同步用户",
|
||||
"sync_wechat_users": "同步企业微信用户",
|
||||
"sync_dingtalk_users": "同步钉钉用户",
|
||||
"sync_lark_users": "同步飞书用户",
|
||||
"sync_complete": "同步完成",
|
||||
"synced_10_users": "成功同步 {num} 个用户",
|
||||
"failed_3_users": "成功同步 {success} 个用户,同步失败 {failed} 个用户,",
|
||||
"download_failure_list": "下载失败列表",
|
||||
"sync_failed": "同步失败",
|
||||
"failed_10_users": "同步失败 {num} 个用户,",
|
||||
"continue_syncing": "继续同步",
|
||||
"return_to_view": "返回查看"
|
||||
},
|
||||
"variables": {
|
||||
"please_select_date": "请选择日期",
|
||||
"number_variable_error": "请输入正确的数值范围",
|
||||
@@ -48,6 +32,25 @@
|
||||
"enter_variable_name": "请输入变量名称",
|
||||
"enter_variable_value": "请输入变量值"
|
||||
},
|
||||
"sync": {
|
||||
"records":"显示 {num} 条数据,共 {total} 条",
|
||||
"confirm_upload":"确定上传",
|
||||
"field_details":"字段详情",
|
||||
"integration": "需开启平台对接",
|
||||
"the_existing_user": "若用户已存在,覆盖旧用户",
|
||||
"sync_users": "同步用户",
|
||||
"sync_wechat_users": "同步企业微信用户",
|
||||
"sync_dingtalk_users": "同步钉钉用户",
|
||||
"sync_lark_users": "同步飞书用户",
|
||||
"sync_complete": "同步完成",
|
||||
"synced_10_users": "成功同步 {num} 个用户",
|
||||
"failed_3_users": "成功同步 {success} 个用户,同步失败 {failed} 个用户,",
|
||||
"download_failure_list": "下载失败列表",
|
||||
"sync_failed": "同步失败",
|
||||
"failed_10_users": "同步失败 {num} 个用户,",
|
||||
"continue_syncing": "继续同步",
|
||||
"return_to_view": "返回查看"
|
||||
},
|
||||
"parameter": {
|
||||
"execution_details": "执行详情",
|
||||
"overview": "概览",
|
||||
@@ -442,6 +445,9 @@
|
||||
"get_schema": "获取Schema"
|
||||
},
|
||||
"model": {
|
||||
"int": "整数",
|
||||
"float": "小数",
|
||||
"string": "字符串",
|
||||
"default_model": "默认模型",
|
||||
"model_type": "模型类型",
|
||||
"basic_model": "基础模型",
|
||||
|
||||
@@ -420,10 +420,13 @@ strong {
|
||||
}
|
||||
}
|
||||
|
||||
.text-variables.text-variables {
|
||||
.text-variables.text-variables,
|
||||
.string-variables.string-variables {
|
||||
color: #3370ff;
|
||||
}
|
||||
.number-variables.number-variables {
|
||||
.number-variables.number-variables,
|
||||
.int-variables.int-variables,
|
||||
.float-variables.float-variables {
|
||||
color: #04b49c;
|
||||
}
|
||||
.datetime-variables.datetime-variables {
|
||||
|
||||
@@ -17,6 +17,8 @@ import EmptyBackground from '@/views/dashboard/common/EmptyBackground.vue'
|
||||
import icon_fileExcel_colorful from '@/assets/datasource/icon_excel.png'
|
||||
import IconOpeDelete from '@/assets/svg/icon_delete.svg'
|
||||
import { useCache } from '@/utils/useCache'
|
||||
import ExcelDetailDialog from './ExcelDetailDialog.vue'
|
||||
import icon_visible_outlined from '@/assets/embedded/icon_visible_outlined.svg'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
@@ -34,6 +36,7 @@ const props = withDefaults(
|
||||
)
|
||||
|
||||
const dsFormRef = ref<FormInstance>()
|
||||
const excelDetailDialogRef = ref<InstanceType<typeof ExcelDetailDialog>>()
|
||||
const emit = defineEmits(['refresh', 'changeActiveStep', 'close'])
|
||||
const isCreate = ref(true)
|
||||
const isEditTable = ref(false)
|
||||
@@ -44,7 +47,7 @@ const tableListLoading = ref(false)
|
||||
const tableListLoadingV1 = ref(false)
|
||||
const checkLoading = ref(false)
|
||||
const dialogTitle = ref('')
|
||||
const getUploadURL = import.meta.env.VITE_API_BASE_URL + '/datasource/uploadExcel'
|
||||
const getUploadURL = import.meta.env.VITE_API_BASE_URL + '/datasource/parseExcel'
|
||||
const saveLoading = ref<boolean>(false)
|
||||
const uploadLoading = ref(false)
|
||||
const { t } = useI18n()
|
||||
@@ -54,7 +57,7 @@ const rules = reactive<FormRules>({
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
message: t('datasource.please_enter') + t('common.empty') + t('ds.form.name'),
|
||||
message: t('datasource.please_enter') + t('common.empty') + t('ds.name'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
{ min: 1, max: 50, message: t('ds.form.validate.name_length'), trigger: 'blur' },
|
||||
@@ -166,9 +169,7 @@ const initForm = (item: any, editTable: boolean = false) => {
|
||||
? configuration.lowVersion
|
||||
: true
|
||||
form.value.ssl =
|
||||
configuration.ssl !== null && configuration.ssl !== undefined
|
||||
? configuration.ssl
|
||||
: false
|
||||
configuration.ssl !== null && configuration.ssl !== undefined ? configuration.ssl : false
|
||||
}
|
||||
|
||||
if (editTable) {
|
||||
@@ -431,15 +432,26 @@ const beforeUpload = (rawFile: any) => {
|
||||
uploadLoading.value = true
|
||||
return true
|
||||
}
|
||||
|
||||
let fileDetail: any = null
|
||||
const onSuccess = (response: any) => {
|
||||
form.value.filename = response.data.filename
|
||||
form.value.sheets = response.data.sheets
|
||||
tableList.value = response.data.sheets
|
||||
fileDetail = response.data
|
||||
excelDetailDialogRef.value?.init(response.data)
|
||||
excelUploadSuccess.value = true
|
||||
uploadLoading.value = false
|
||||
}
|
||||
|
||||
const openFile = () => {
|
||||
onSuccess({
|
||||
data: fileDetail,
|
||||
})
|
||||
}
|
||||
|
||||
const saveExcel = (excel: any) => {
|
||||
form.value.filename = excel.filename
|
||||
form.value.sheets = excel.sheets
|
||||
tableList.value = excel.sheets
|
||||
}
|
||||
|
||||
const onError = (e: any) => {
|
||||
ElMessage.error(e.toString())
|
||||
uploadLoading.value = false
|
||||
@@ -552,6 +564,26 @@ defineExpose({
|
||||
:rules="rules"
|
||||
@submit.prevent
|
||||
>
|
||||
<el-form-item :label="t('ds.name')" prop="name">
|
||||
<el-input
|
||||
v-model="form.name"
|
||||
clearable
|
||||
:placeholder="$t('datasource.please_enter') + $t('common.empty') + t('ds.name')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('ds.form.description')">
|
||||
<el-input
|
||||
v-model="form.description"
|
||||
:placeholder="
|
||||
$t('datasource.please_enter') + $t('common.empty') + t('ds.form.description')
|
||||
"
|
||||
:rows="2"
|
||||
show-word-limit
|
||||
maxlength="200"
|
||||
clearable
|
||||
type="textarea"
|
||||
/>
|
||||
</el-form-item>
|
||||
<div v-if="form.type === 'excel'">
|
||||
<el-form-item prop="sheets" :label="t('ds.form.file')">
|
||||
<div v-if="form.filename" class="pdf-card">
|
||||
@@ -560,9 +592,22 @@ defineExpose({
|
||||
<div class="name">{{ form.filename }}</div>
|
||||
<div class="size">{{ form.filename.split('.')[1] }} - {{ fileSize }}</div>
|
||||
</div>
|
||||
<el-icon v-if="!form.id" class="action-btn" size="16" @click="clearFile">
|
||||
<IconOpeDelete></IconOpeDelete>
|
||||
</el-icon>
|
||||
<div
|
||||
style="
|
||||
width: 40px;
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
"
|
||||
>
|
||||
<el-icon v-if="!form.id" class="action-btn" size="16" @click="openFile">
|
||||
<icon_visible_outlined></icon_visible_outlined>
|
||||
</el-icon>
|
||||
<el-icon v-if="!form.id" class="action-btn" size="16" @click="clearFile">
|
||||
<IconOpeDelete></IconOpeDelete>
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
<el-upload
|
||||
v-if="form.filename && !form.id"
|
||||
@@ -602,26 +647,6 @@ defineExpose({
|
||||
<span v-if="!form.filename" class="not_exceed">{{ $t('common.not_exceed_50mb') }}</span>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<el-form-item :label="t('ds.form.name')" prop="name">
|
||||
<el-input
|
||||
v-model="form.name"
|
||||
clearable
|
||||
:placeholder="$t('datasource.please_enter') + $t('common.empty') + t('ds.form.name')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('ds.form.description')">
|
||||
<el-input
|
||||
v-model="form.description"
|
||||
:placeholder="
|
||||
$t('datasource.please_enter') + $t('common.empty') + t('ds.form.description')
|
||||
"
|
||||
:rows="2"
|
||||
show-word-limit
|
||||
maxlength="200"
|
||||
clearable
|
||||
type="textarea"
|
||||
/>
|
||||
</el-form-item>
|
||||
<div v-if="form.type !== 'excel'" style="margin-top: 16px">
|
||||
<el-form-item
|
||||
:label="form.type !== 'es' ? t('ds.form.host') : t('ds.form.address')"
|
||||
@@ -694,11 +719,7 @@ defineExpose({
|
||||
>
|
||||
<el-checkbox v-model="form.lowVersion" :label="t('ds.form.low_version')" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="form.type === 'mysql'"
|
||||
:label="t('ds.form.ssl')"
|
||||
prop="ssl"
|
||||
>
|
||||
<el-form-item v-if="form.type === 'mysql'" :label="t('ds.form.ssl')" prop="ssl">
|
||||
<el-switch v-model="form.ssl" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.type !== 'es'" :label="t('ds.form.extra_jdbc')">
|
||||
@@ -829,6 +850,7 @@ defineExpose({
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<ExcelDetailDialog ref="excelDetailDialogRef" @finish="saveExcel" />
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@@ -898,10 +920,6 @@ defineExpose({
|
||||
}
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.ed-icon {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
|
||||
@@ -0,0 +1,276 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, shallowRef } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import SheetTabs from './SheetTabs.vue'
|
||||
import field_text from '@/assets/svg/field_text.svg'
|
||||
import field_time from '@/assets/svg/field_time.svg'
|
||||
import field_value from '@/assets/svg/field_value.svg'
|
||||
import { datasourceApi } from '@/api/datasource'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const emits = defineEmits(['finish'])
|
||||
const tabList = shallowRef([])
|
||||
const fieldList = shallowRef([])
|
||||
const iconMap = {
|
||||
string: field_text,
|
||||
int: field_value,
|
||||
float: field_value,
|
||||
datetime: field_time,
|
||||
}
|
||||
const dialogShow = ref(false)
|
||||
const loading = ref(false)
|
||||
const previewData = ref<any>({})
|
||||
|
||||
const variables = [
|
||||
{
|
||||
name: t('model.text'),
|
||||
var_type: 'string',
|
||||
},
|
||||
{
|
||||
name: t('model.int'),
|
||||
var_type: 'int',
|
||||
},
|
||||
{
|
||||
name: t('model.float'),
|
||||
var_type: 'float',
|
||||
},
|
||||
{
|
||||
name: t('dashboard.time'),
|
||||
var_type: 'datetime',
|
||||
},
|
||||
]
|
||||
|
||||
let arr: any = []
|
||||
let filePath = ''
|
||||
const init = (response: any) => {
|
||||
arr = response.data || []
|
||||
filePath = response.filePath
|
||||
previewData.value = response.data[0]
|
||||
activeTab.value = previewData.value.sheetName
|
||||
fieldList.value = previewData.value.fields
|
||||
tabList.value = response.data.map((item: any) => {
|
||||
return {
|
||||
label: item.sheetName,
|
||||
value: item.sheetName,
|
||||
}
|
||||
})
|
||||
dialogShow.value = true
|
||||
}
|
||||
|
||||
const closeDialog = () => {
|
||||
arr = []
|
||||
previewData.value = {
|
||||
sheetName: '',
|
||||
fields: [],
|
||||
data: [],
|
||||
rows: 0,
|
||||
}
|
||||
tabList.value = []
|
||||
fieldList.value = []
|
||||
activeTab.value = ''
|
||||
btnSelect.value = 'd'
|
||||
dialogShow.value = false
|
||||
}
|
||||
|
||||
const save = () => {
|
||||
loading.value = true
|
||||
datasourceApi
|
||||
.importToDb({
|
||||
sheets: arr.map((item: any) => {
|
||||
return { fields: item.fields, sheetName: item.sheetName }
|
||||
}),
|
||||
filePath,
|
||||
})
|
||||
.then((res: any) => {
|
||||
closeDialog()
|
||||
emits('finish', res)
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
const btnSelect = ref('d')
|
||||
const btnSelectClick = (val: any) => {
|
||||
btnSelect.value = val
|
||||
}
|
||||
const activeTab = ref('')
|
||||
const handleTabClick = (tab: any) => {
|
||||
btnSelectClick('d')
|
||||
activeTab.value = tab.value
|
||||
arr.forEach((item: any) => {
|
||||
if (item.sheetName === activeTab.value) {
|
||||
previewData.value = item
|
||||
}
|
||||
})
|
||||
fieldList.value = previewData.value.fields
|
||||
}
|
||||
|
||||
const renderHeader = ({ column }: any) => {
|
||||
//创建一个元素用于存放表头信息
|
||||
const span = document.createElement('span')
|
||||
// 将表头信息渲染到元素上
|
||||
span.innerText = column.label
|
||||
// 在界面中添加该元素
|
||||
document.body.appendChild(span)
|
||||
//获取该元素的宽度(包含内外边距等信息)
|
||||
const spanWidth = span.getBoundingClientRect().width + 20 //渲染后的 div 内左右 padding 都是 10,所以 +20
|
||||
//判断是否小于element的最小宽度,两者取最大值
|
||||
column.minWidth = column.minWidth > spanWidth ? column.minWidth : spanWidth
|
||||
// 计算完成后,删除该元素
|
||||
document.body.removeChild(span)
|
||||
return column.label
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
init,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="dialogShow"
|
||||
:title="$t('ds.preview')"
|
||||
width="1200"
|
||||
modal-class="excel-detail-dialog"
|
||||
destroy-on-close
|
||||
:close-on-click-modal="false"
|
||||
@before-closed="closeDialog"
|
||||
>
|
||||
<SheetTabs :active-tab="activeTab" :tab-list="tabList" @tab-click="handleTabClick"></SheetTabs>
|
||||
<div v-loading="loading" class="content">
|
||||
<div class="btn-select" style="margin: 8px 12px">
|
||||
<el-button :class="[btnSelect === 'd' && 'is-active']" text @click="btnSelectClick('d')">
|
||||
{{ t('ds.preview') }}
|
||||
</el-button>
|
||||
<el-button :class="[btnSelect === 'q' && 'is-active']" text @click="btnSelectClick('q')">
|
||||
{{ t('sync.field_details') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="preview-num">
|
||||
{{ t('sync.records', { num: previewData.data.length, total: previewData.rows }) }}
|
||||
</div>
|
||||
|
||||
<div v-if="btnSelect === 'q'" class="field-details">
|
||||
<el-table row-class-name="hover-icon_edit" :data="fieldList" style="width: 100%">
|
||||
<el-table-column prop="fieldName" :label="t('datasource.field_name')" />
|
||||
<el-table-column prop="fieldType" :label="t('datasource.field_type')" width="240">
|
||||
<template #default="scope">
|
||||
<el-icon
|
||||
:class="`${scope.row.fieldType}-variables`"
|
||||
size="16"
|
||||
style="position: absolute; top: 20px; left: 32px; z-index: 10"
|
||||
>
|
||||
<component :is="iconMap[scope.row.fieldType as keyof typeof iconMap]"></component>
|
||||
</el-icon>
|
||||
<el-select
|
||||
v-model="scope.row.fieldType"
|
||||
style="max-width: 197px; margin-left: 8px"
|
||||
:placeholder="t('datasource.Please_select')"
|
||||
>
|
||||
<el-option
|
||||
v-for="ele in variables"
|
||||
:key="ele.var_type"
|
||||
:label="ele.name"
|
||||
:value="ele.var_type"
|
||||
>
|
||||
<div style="width: 100%; display: flex; align-items: center">
|
||||
<el-icon
|
||||
:class="`${ele.var_type}-variables`"
|
||||
size="16"
|
||||
style="margin-right: 4px"
|
||||
>
|
||||
<component :is="iconMap[ele.var_type as keyof typeof iconMap]"></component>
|
||||
</el-icon>
|
||||
{{ ele.name }}
|
||||
</div>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<div v-else class="preview">
|
||||
<div class="table-container">
|
||||
<el-table :data="previewData.data" style="width: 100%; height: 100%">
|
||||
<el-table-column
|
||||
v-for="(c, index) in previewData.fields"
|
||||
:key="index"
|
||||
:prop="c.fieldName"
|
||||
:label="c.fieldName"
|
||||
min-width="150"
|
||||
:render-header="renderHeader"
|
||||
/>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; justify-content: flex-end; margin-top: 20px">
|
||||
<el-button secondary @click="closeDialog">{{ $t('common.cancel') }}</el-button>
|
||||
<el-button v-loading="loading" type="primary" @click="save">{{
|
||||
$t('sync.confirm_upload')
|
||||
}}</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<style lang="less">
|
||||
.excel-detail-dialog {
|
||||
.content {
|
||||
height: 593px;
|
||||
background-color: #f5f6f7;
|
||||
border: 1px solid #dee0e3;
|
||||
border-radius: 6px;
|
||||
margin-top: -1px;
|
||||
border-top-left-radius: 0;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
.field-details {
|
||||
.ed-select__wrapper {
|
||||
padding-left: 32px;
|
||||
}
|
||||
}
|
||||
.btn-select {
|
||||
height: 32px;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
display: inline-flex;
|
||||
background: #ffffff;
|
||||
align-items: center;
|
||||
border: 1px solid #d0d3d6;
|
||||
border-radius: 6px;
|
||||
|
||||
.is-active {
|
||||
background: var(--ed-color-primary-1a, #1cba901a);
|
||||
}
|
||||
|
||||
.ed-button:not(.is-active) {
|
||||
color: #1f2329;
|
||||
}
|
||||
.ed-button.is-text {
|
||||
height: 24px;
|
||||
width: auto;
|
||||
padding: 0 8px;
|
||||
line-height: 24px;
|
||||
}
|
||||
.ed-button + .ed-button {
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.preview-num {
|
||||
margin: 0 0 8px 12px;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
color: #646a73;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
width: 100%;
|
||||
height: calc(100% - 46px);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,216 @@
|
||||
<script lang="ts" setup>
|
||||
import icon_expandLeft_filled from '@/assets/svg/icon_expand-left_filled.svg'
|
||||
import icon_expandRight_filled from '@/assets/svg/icon_expand-right_filled.svg'
|
||||
import { toRefs, ref, watch, nextTick } from 'vue'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
const props = defineProps({
|
||||
tabList: propTypes.arrayOf(
|
||||
propTypes.shape({
|
||||
label: String,
|
||||
value: String,
|
||||
})
|
||||
),
|
||||
activeTab: propTypes.string.def(''),
|
||||
})
|
||||
|
||||
const activeTabIndex = ref(0)
|
||||
|
||||
const emits = defineEmits(['TabClick'])
|
||||
const { activeTab } = toRefs(props)
|
||||
const handleTabClick = (tab: any) => {
|
||||
let tabDom = document.getElementById(`tab-${tab.value}`)
|
||||
if (tabDom!.offsetLeft + tabDom!.offsetWidth > tabWrapper.value.offsetWidth) {
|
||||
tabWrapper.value.scrollLeft =
|
||||
tabDom!.offsetLeft + tabDom!.offsetWidth - tabWrapper.value.offsetWidth
|
||||
} else {
|
||||
tabWrapper.value.scrollLeft = 0
|
||||
}
|
||||
emits('TabClick', tab)
|
||||
}
|
||||
const tabWrapper = ref()
|
||||
const showBtn = ref(false)
|
||||
watch(
|
||||
() => activeTab.value,
|
||||
(val) => {
|
||||
activeTabIndex.value = props.tabList!.findIndex((ele) => ele.value === val)
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.tabList,
|
||||
() => {
|
||||
nextTick(() => {
|
||||
showBtn.value = tabWrapper.value.scrollWidth > tabWrapper.value.offsetWidth
|
||||
})
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
const prevClick = () => {
|
||||
let domWrapper = tabWrapper.value
|
||||
if (!domWrapper.scrollLeft) return
|
||||
domWrapper.scrollLeft -= 30
|
||||
}
|
||||
|
||||
const nextClick = () => {
|
||||
let domWrapper = tabWrapper.value
|
||||
domWrapper.scrollLeft += 30
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="sheet-tabs">
|
||||
<div ref="tabWrapper" class="tab-wrapper">
|
||||
<div
|
||||
v-for="tab in tabList"
|
||||
:id="`tab-${tab.value}`"
|
||||
:key="tab.label"
|
||||
:title="tab.label"
|
||||
:class="[{ active: activeTab === tab.value }, 'sheet-tab']"
|
||||
@click="handleTabClick(tab)"
|
||||
>
|
||||
<span class="ellipsis">
|
||||
{{ tab.label }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="showBtn" class="tab-btn">
|
||||
<el-icon size="12px" @click="prevClick">
|
||||
<icon_expandLeft_filled />
|
||||
</el-icon>
|
||||
<el-icon size="12px" @click="nextClick">
|
||||
<icon_expandRight_filled />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.sheet-tabs {
|
||||
border-top-left-radius: 3px;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
padding-right: 60px;
|
||||
z-index: 10;
|
||||
|
||||
.tab-wrapper {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
overflow-x: auto;
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.tab-btn {
|
||||
padding: 8px 12px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 60px;
|
||||
height: 28px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 4px;
|
||||
background: #fff;
|
||||
|
||||
.ed-icon {
|
||||
color: #8d9199;
|
||||
cursor: pointer;
|
||||
|
||||
&.disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
&:not(.disabled):hover {
|
||||
color: var(--ed-color-primary);
|
||||
}
|
||||
|
||||
& + .ed-icon {
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sheet-tab {
|
||||
color: #1f2329;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
padding: 0 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 36px;
|
||||
max-width: 200px;
|
||||
&:hover {
|
||||
color: var(--ed-color-primary);
|
||||
}
|
||||
|
||||
.ellipsis {
|
||||
max-width: 200px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
&::after,
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
height: 24px;
|
||||
width: 1px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
background: rgba(31, 35, 41, 0.15);
|
||||
}
|
||||
|
||||
&::after {
|
||||
right: 0;
|
||||
}
|
||||
&::before {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
& + .active {
|
||||
::before {
|
||||
content: '';
|
||||
left: -3px;
|
||||
height: 30px;
|
||||
width: 2px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
.active {
|
||||
box-shadow: 0px -1px 0px 0px #f5f6f7 inset;
|
||||
color: var(--ed-color-primary);
|
||||
border: 1px solid rgba(31, 35, 41, 0.15);
|
||||
border-bottom: none;
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
background: #f5f6f7;
|
||||
position: relative;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
height: 3px;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
top: calc(100% - 1px);
|
||||
left: 0;
|
||||
background: #f5f6f7;
|
||||
display: block;
|
||||
}
|
||||
|
||||
& + .sheet-tab {
|
||||
&::before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user