添加组件

master
suixy 1 month ago
parent 0dca917d8c
commit ebb73dd7a7

@ -0,0 +1,434 @@
<template>
<div class="param-container" style="padding: 5vw 10vw;background-color: #0001">
<h2 class="title1" style="line-height: 40px">产品参数</h2>
<div class="paramsTable" v-for="(param, index) in data.params" :key="index" style="position: relative;">
<i class="del el-icon-circle-close" @click="data.params.splice(index, 1);"></i>
<div class="th">
<div class="th1" :style="nameColStyle(param)" contenteditable="true" @blur="editTableTitle(index,$event)">
{{ param.title }}
</div>
<div
class="th2"
v-for="(col, cIdx) in getColumns(param)"
:key="'c'+cIdx"
:style="valueColStyle(param)"
contenteditable="true"
@blur="editColumn(index, cIdx, $event)">
{{ col || '参数值' }}
<i
v-if="getColumns(param).length > 1"
class="del el-icon-circle-close"
style="right: 6px; top: 6px"
@click.stop="delColumn(index, cIdx)"></i>
</div>
<div class="th-actions">
<el-tooltip content="添加列" placement="top">
<i class="el-icon-circle-plus" style="cursor: pointer;" @click="addColumn(index)"></i>
</el-tooltip>
</div>
</div>
<div class="td">
<div v-for="(row, rIdx) in getRows(param)" :key="'r'+rIdx" style="position: relative;">
<i class="del el-icon-circle-close" @click="delRow(index, rIdx)"></i>
<div class="td1" :style="nameColStyle(param)" contenteditable="true" @blur="editRowName(index,rIdx,$event)">
{{ row.name }}
</div>
<template v-if="!isMergedRow(row)">
<div
class="td2"
v-for="(col, cIdx) in getColumns(param)"
:key="'v'+cIdx"
:style="valueColStyle(param)"
contenteditable="true"
@blur="editCell(index,rIdx,cIdx,$event)">{{ getCellValue(row, cIdx) }}
</div>
</template>
<template v-else>
<div class="td2 td2-merged" :style="mergedColStyle(param)" contenteditable="true"
@blur="editCell(index,rIdx,0,$event)">{{ getCellValue(row, 0) }}
</div>
</template>
<div class="row-actions">
<el-switch v-model="data.params[index].rows[rIdx].merge" active-text=""
inactive-text="分列"></el-switch>
</div>
</div>
</div>
<div class="td">
<div class="td1" :style="nameColStyle(param)" @click="addRow(index)" style="cursor: pointer;">
<i class="el-icon-circle-plus" style="font-size: 1vw; line-height: 60px"></i>
添加行
</div>
<div class="td2" :style="mergedColStyle(param)" style="cursor: pointer;" @click="openImport(index)">
<i class="el-icon-upload" style="font-size: 1vw; line-height: 60px"></i>
粘贴导入表格
</div>
</div>
<div class="tools">
<span>参数名列宽</span>
<el-slider :min="20" :max="50" :step="5" v-model="data.params[index].nameColPercent"></el-slider>
</div>
</div>
<div class="td" @click="addTable()">
<i class="el-icon-circle-plus" style="font-size: 1vw; line-height: 60px"></i>
新增参数表
</div>
<el-dialog title="粘贴导入表格" :visible.sync="importDlg" width="50%">
<div style="text-align: left; line-height: 24px; color: #666; margin-bottom: 8px;">
你可以先用其他AI/工具识别图片中的表格推荐使用豆包/KIMI让它按以下任一格式输出随后直接粘贴到下方
1) 使用 <b>Tab(\t)</b> <b>竖线(|)</b> <b>逗号(,)</b> 分隔列
2) <b>首行第一列为表名</b>后续为列标题
3) <b>每行第一列为参数名</b>后续列为参数值
4) 或者直接输出 <b>HTML &lt;table&gt;</b> 标签结构
</div>
<el-input type="textarea" v-model="importText"
placeholder="在此粘贴表格内容可为AI识别文本、TAB/逗号/空格/冒号/竖线分隔或HTML表格" rows="10"/>
<div style="margin-top: 10px; text-align: left;">
<el-form label-width="110px" inline>
<el-form-item label="分隔符">
<el-select v-model="importOptions.delimiter" style="width: 180px">
<el-option label="自动识别" value="auto"/>
<el-option label="Tab" value="tab"/>
<el-option label="逗号 ," value="comma"/>
<el-option label="空格(≥2个)" value="space2"/>
<el-option label="冒号 :/" value="colon"/>
<el-option label="竖线 |" value="pipe"/>
<el-option label="分号 ;" value="semicolon"/>
</el-select>
</el-form-item>
<el-form-item label="首行是列名">
<el-switch v-model="importOptions.header"/>
</el-form-item>
<el-form-item label="合并行检测">
<el-switch v-model="importOptions.detectMerged"/>
</el-form-item>
</el-form>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="importDlg=false"> </el-button>
<el-button type="primary" @click="applyImport()"></el-button>
</span>
</el-dialog>
</div>
</template>
<script>
export default {
name: 'PlatformFeatures',
props: ['data'],
components: {},
data() {
return {
importDlg: false,
importText: '',
importingIndex: -1,
importOptions: {delimiter: 'auto', header: true, detectMerged: true},
}
},
mounted() {
this.ensureTableStructure()
},
methods: {
ensureTableStructure() {
const params = this.$props.data?.params || []
params.forEach(p => {
if (!p.columns) {
p.columns = ['参数值']
}
if (!p.rows && p.list) {
p.rows = (p.list || []).map(i => ({name: i.name || '', values: [i.value || '']}))
delete p.list
}
if (!p.rows) {
p.rows = []
}
})
},
getColumns(param) {
return param.columns && param.columns.length ? param.columns : ['参数值']
},
getRows(param) {
return param.rows || []
},
nameColPercent(param) {
return typeof param.nameColPercent === 'number' ? param.nameColPercent : 30
},
nameColStyle(param) {
const p = this.nameColPercent(param)
return {width: `calc(${p}% - 2vw - 2px)`}
},
valueColStyle(param) {
const p = this.nameColPercent(param)
const n = (param.columns && param.columns.length) ? param.columns.length : 1
const right = 100 - p
const totalPadVw = n * 2 // padding-left: 2vw
const totalBorderPx = n * 2 // 2px
return {width: `calc((${right}% - ${totalPadVw}vw - ${totalBorderPx}px)/${n})`}
},
mergedColStyle(param) {
const p = this.nameColPercent(param)
const right = 100 - p
return {width: `calc(${right}% - 2vw - 2px)`}
},
getCellValue(row, cIdx) {
if (Array.isArray(row.values)) return row.values[cIdx] || ''
//
return cIdx === 0 ? (row.value || '') : ''
},
isMergedRow(row) {
return !!row.merge || (Array.isArray(row.values) ? row.values.length <= 1 : true)
},
// -------- editing --------
editTableTitle(index, e) {
this.$props.data.params[index].title = e.target.innerText
},
editColumn(index, cIdx, e) {
this.$props.data.params[index].columns[cIdx] = e.target.innerText
},
editRowName(index, rIdx, e) {
this.$props.data.params[index].rows[rIdx].name = e.target.innerText
},
editCell(index, rIdx, cIdx, e) {
const row = this.$props.data.params[index].rows[rIdx]
if (!Array.isArray(row.values)) row.values = []
row.values[cIdx] = e.target.innerText
},
addTable() {
this.$props.data.params.push({title: '', columns: ['参数值'], rows: []})
},
addColumn(index) {
const p = this.$props.data.params[index]
if (!p.columns) this.$set(p, 'columns', ['参数值'])
p.columns.push('')
(p.rows || []).forEach(r => {
if (!Array.isArray(r.values)) this.$set(r, 'values', Array(p.columns.length - 1).fill(''))
r.values.push('')
})
},
delColumn(index, cIdx) {
const p = this.$props.data.params[index]
if ((p.columns || []).length <= 1) return
p.columns.splice(cIdx, 1)
(p.rows || []).forEach(r => {
if (Array.isArray(r.values)) r.values.splice(cIdx, 1)
})
},
addRow(index) {
const p = this.$props.data.params[index]
const n = (p.columns && p.columns.length) ? p.columns.length : 1
const values = Array(n).fill('')
p.rows.push({name: '', values})
},
delRow(index, rIdx) {
this.$props.data.params[index].rows.splice(rIdx, 1)
},
// -------- import --------
openImport(index) {
this.importingIndex = index
this.importText = ''
this.importDlg = true
},
applyImport() {
console.log(1)
const text = (this.importText || '').trim()
if (!text) {
this.importDlg = false
return
}
const parsed = this.parseTableText(text, this.importOptions)
console.log(parsed)
if (!parsed) {
this.importDlg = false
return
}
const p = this.$props.data.params[this.importingIndex]
if (parsed.title) p.title = parsed.title
p.columns = parsed.columns && parsed.columns.length ? parsed.columns : ['参数值']
p.rows = parsed.rows || []
this.importDlg = false
},
// HTMLExcelOCR
parseTableText(text, opts = {delimiter: 'auto', header: true, detectMerged: true}) {
text = this.normalizeText(text)
// HTML
if (text.includes('<table')) {
try {
const div = document.createElement('div')
div.innerHTML = text
const table = div.querySelector('table')
if (table) {
const rows = Array.from(table.querySelectorAll('tr')).map(tr => Array.from(tr.querySelectorAll('th,td')).map(td => td.textContent.trim()))
return this.rowsToSchema(rows, {header: opts.header, detectMerged: opts.detectMerged, delimiter: 'html'})
}
} catch (e) {
}
}
const lines = text.split(/\r?\n/).map(l => l.trim()).filter(l => l.length)
// 使
const delim = opts.delimiter === 'auto' ? this.detectDelimiter(lines) : opts.delimiter
let rows
if (delim === 'tab') {
rows = lines.map(l => l.split(/\t/).map(c => c.trim()))
} else if (delim === 'comma') {
rows = lines.map(l => l.split(/,/).map(c => c.trim()))
} else if (delim === 'space2') {
rows = lines.map(l => l.split(/\s{2,}/).map(c => c.trim()))
} else if (delim === 'pipe') {
rows = lines.map(l => l.split(/\|/).map(c => c.trim()))
} else if (delim === 'semicolon') {
rows = lines.map(l => l.split(/;/).map(c => c.trim()))
} else if (delim === 'colon') {
rows = lines.map(l => {
const m = l.split(/:\s*|\s*/)
if (m.length >= 2) {
const name = m[0]
const vals = m.slice(1).join(' ').split(/[\s,,、]+/).filter(Boolean)
return [name, ...vals]
}
return [l]
})
} else {
// TAB
rows = lines.map(l => l.split(/\t|,/).map(c => c.trim()))
}
return this.rowsToSchema(rows, {header: opts.header, detectMerged: opts.detectMerged, delimiter: delim})
},
normalizeText(text) {
return text
.replace(/\u3000/g, ' ') //
.replace(//g, ',')
.replace(//g, ':')
.replace(/[\u2013\u2014\u2015]/g, '-')
.replace(/[\t ]+\|[\t ]+/g, '|')
},
detectDelimiter(lines) {
const hasTab = lines.filter(l => /\t/.test(l)).length
const hasPipe = lines.filter(l => /\|/.test(l)).length
const hasSemicolon = lines.filter(l => /;/.test(l)).length
const hasComma = lines.filter(l => /,/.test(l)).length
const hasColon = lines.filter(l => /[:]/.test(l)).length
const hasSpace2 = lines.filter(l => /\s{2,}/.test(l)).length
const total = lines.length
if (hasTab > total * 0.5) return 'tab'
if (hasPipe > total * 0.5) return 'pipe'
if (hasSemicolon > total * 0.5) return 'semicolon'
if (hasColon > total * 0.5) return 'colon'
if (hasSpace2 > total * 0.5) return 'space2'
if (hasComma > total * 0.5) return 'comma'
return 'space2'
},
rowsToSchema(rows, {header = true, detectMerged = true, delimiter = 'auto'} = {}) {
if (!rows || !rows.length) return null
let title = ''
let columns = ['参数值']
let bodyRows = rows
if (header) {
const headerRow = rows[0]
title = headerRow[0] || ''
columns = headerRow.slice(1)
bodyRows = rows.slice(1)
if (!columns.length) columns = ['参数值']
}
const parsedRows = bodyRows.map(r => {
const name = r[0] || ''
const values = r.slice(1)
const merge = detectMerged && (values.length <= 1)
return {name, values: values.length ? values : [''], merge}
})
return {title, columns, rows: parsedRows}
},
}
}
</script>
<style lang="less" scoped>
@import "~@/style.less";
.paramsTable {
.th {
position: relative;
background-color: #DBDFE7;
line-height: 3vw;
font-size: 1.3vw;
font-weight: 700;
text-align: left;
.th1 {
display: inline-block;
padding-left: 2vw;
border-right: 1px solid #0001
}
.th2 {
/* 动态宽度在行内样式计算 */
display: inline-block;
padding-left: 2vw;
position: relative;
box-sizing: border-box;
vertical-align: top;
}
.th-actions {
position: absolute;
right: 12px;
top: 6px;
}
}
.td {
line-height: 3vw;
font-size: 0.9vw;
text-align: left;
.td1 {
background-color: #F2F4F8;
display: inline-block;
padding-left: 2vw;
border: 1px solid #0001;
box-sizing: border-box;
vertical-align: top;
}
.td2 {
background-color: #EEEEEE;
/* 动态宽度在行内样式计算 */
display: inline-block;
padding-left: 2vw;
border: 1px solid #0001;
box-sizing: border-box;
vertical-align: top;
}
.row-actions {
position: absolute;
right: 12px;
top: 6px;
}
}
}
.tools {
text-align: left;
padding: 10px 2vw;
}
.del {
position: absolute;
right: 0;
top: 0;
cursor: pointer;
}
</style>

@ -0,0 +1,111 @@
<template>
<div class="param-container" style="padding: 5vw 10vw;background-color: #0001">
<h2 class="title1" style="line-height: 40px">产品参数</h2>
<div class="paramsTable" v-for="(param, index) in data.params" :key="index" style="position: relative;">
<i class="del el-icon-circle-close" @click="data.params.splice(index, 1);"></i>
<div class="th">
<div class="th1" contenteditable="true" @blur="edit3('title',index,$event)">{{ param.title }}</div>
<div class="th2">参数值</div>
</div>
<div class="td">
<div v-for="(item, idx) in param.list" :key="idx" style="position: relative;">
<i class="del el-icon-circle-close" @click="data.params[index].list.splice(idx, 1);"></i>
<div class="td1" contenteditable="true" @blur="edit4('name',index,idx,$event)">{{ item.name }}</div>
<div class="td2" contenteditable="true" @blur="edit4('value',index,idx,$event)">{{ item.value }}</div>
</div>
</div>
<div class="td">
<div class="td1"
@click="data.params[index].list.push({name:'',value:''})">
<i class="el-icon-circle-plus" style="font-size: 1vw; line-height: 60px"></i>
</div>
</div>
</div>
<div class="td"
@click="data.params.push({title:'',list:[]})">
<i class="el-icon-circle-plus" style="font-size: 1vw; line-height: 60px"></i>
</div>
</div>
</template>
<script>
import UploadEl from "@/components/editEl/uploadEl.vue";
export default {
name: 'PlatformFeatures',
props: ['data'],
components: {UploadEl},
data() {
return {}
},
mounted() {
},
methods: {
edit3(key, index, e) {
this.$props.data.params[index][key] = e.target.innerText
},
edit4(key, index, index1, e) {
this.$props.data.params[index].list[index1][key] = e.target.innerText
},
}
}
</script>
<style lang="less" scoped>
@import "~@/style.less";
.paramsTable {
.th {
background-color: #DBDFE7;
line-height: 3vw;
font-size: 1.3vw;
font-weight: 700;
text-align: left;
.th1 {
width: calc(30% - 2vw - 2px);
display: inline-block;
padding-left: 2vw;
border-right: 1px solid #0001
}
.th2 {
width: calc(70% - 2vw - 2px);
display: inline-block;
padding-left: 2vw;
}
}
.td {
line-height: 3vw;
font-size: 0.9vw;
text-align: left;
.td1 {
background-color: #F2F4F8;
width: calc(30% - 2vw - 2px);
display: inline-block;
padding-left: 2vw;
border: 1px solid #0001
}
.td2 {
background-color: #EEEEEE;
width: calc(70% - 2vw - 2px);
display: inline-block;
padding-left: 2vw;
border: 1px solid #0001
}
}
}
.del {
position: absolute;
right: 0;
top: 0;
cursor: pointer;
}
</style>

@ -0,0 +1,325 @@
<template>
<div class="param-container" style="padding: 5vw 10vw;background-color: #0001">
<h2 class="title1" style="line-height: 40px">
<span>
产品参数
</span>
</h2>
<div class="paramsTable" v-for="(param, index) in data.params" :key="index" style="position: relative;">
<el-button type="primary" @click="openImport(index)"></el-button>
<i class="del el-icon-circle-close" @click="data.params.splice(index, 1);"></i>
<div class="th">
<div class="th1" contenteditable="true" @blur="edit3('title',index,$event)">{{ param.title }}</div>
<div class="th2" v-for="(i,index1) in param.columns"
:style="{width: `calc((70% - 2vw - 2px )/ ${(param.columns.length || 1 ) } )`,paddingLeft:` ${2 / ((param.columns.length || 1) )}vw`}">
<span style="display: inline-block;width: 100%;height: 100%" contenteditable="true"
@blur="edit2(index,index1,$event)"> {{
i
}}</span>
<i class="del el-icon-circle-close" @click.stop="delDevice(index,index1)"></i>
</div>
<div class="th2"
:style="{position:'absolute',left:'100%',width: `5vw`,lineHeight:'2vw',paddingLeft:` ${2 / ((param.columns.length || 1) )}vw`}"
@click="data.params[index].columns.push('新设备');addDeviceInfo(index)">
<i class="el-icon-circle-plus" style="font-size: 1vw; line-height: 3vw"></i>
</div>
</div>
<div class="td">
<div v-for="(item, idx) in param.rows" :key="idx" style="position: relative;display: flex">
<div
:style="{position:'absolute',left:'100%',width: `300px`,lineHeight:'2vw',paddingLeft:` ${2 / ((param.columns.length || 1) )}vw`}"
>
<el-switch
@change="mergeChange(index,idx,$event)"
v-model="item.merge"
active-text="合并"
inactive-text="独立">
</el-switch>
</div>
<i class="del el-icon-circle-close" @click="data.params[index].rows.splice(idx, 1);"></i>
<div class="td1" contenteditable="true" @blur="edit4('name',index,idx,$event)">{{ item.name }}</div>
<div class="td2" v-if="!item.merge"
:style="{width: `calc((70% - 2vw - 4px )/ ${item.values.length || 1 } )`,paddingLeft:` ${2 / (item.values.length || 1)}vw`}"
v-for="(i,k) in item.values" contenteditable="true" @blur="edit5(index,idx,k,$event)">{{
i
}}
</div>
<div class="td2" v-if="item.merge"
:style="{width: `calc(70% - 2vw - 4px ) `,paddingLeft:` 2vw`}"
contenteditable="true" @blur="edit6(index,idx,$event)">{{
item.values[0]
}}
</div>
</div>
</div>
<div class="td">
<div class="td1"
:style="{
}"
@click="addRows(index)">
<i class="el-icon-circle-plus" style="font-size: 1vw; line-height: 60px"></i>
</div>
</div>
</div>
<div class="td"
@click="addParams">
<i class="el-icon-circle-plus" style="font-size: 1vw; line-height: 60px"></i>
</div>
<el-dialog title="粘贴导入表格" :visible.sync="importDlg" width="50%">
<div style="text-align: left; line-height: 24px; color: #666; margin-bottom: 8px;">
你可以先用其他AI/工具识别图片中的表格推荐使用豆包/KIMI让它按以下任一格式输出随后直接粘贴到下方
1) 使用 <b>Tab(\t)</b> <b>竖线(|)</b> <b>逗号(,)</b> 分隔列
2) <b>首行第一列为表名</b>后续为列标题
3) <b>每行第一列为参数名</b>后续列为参数值
4) 或者直接输出 <b>HTML &lt;table&gt;</b> 标签结构
</div>
<el-input type="textarea" v-model="importText"
placeholder="在此粘贴表格内容可为AI识别文本、TAB/逗号/空格/冒号/竖线分隔或HTML表格" rows="10"/>
<span slot="footer" class="dialog-footer">
<el-button @click="importDlg=false"> </el-button>
<el-button type="primary" @click="applyImport()"></el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import UploadEl from "@/components/editEl/uploadEl.vue";
export default {
name: 'PlatformFeatures',
props: ['data'],
components: {UploadEl},
data() {
return {
importDlg: false,
importText: '',
importingIndex: -1,
importOptions: {delimiter: 'auto', header: true, detectMerged: true},
}
},
mounted() {
},
watch: {},
methods: {
delDevice(index, k) {
if (this.$props.data.params[index].columns.length > 1) {
this.$nextTick(() => {
this.$props.data.params[index].columns.splice(k, 1)
this.$nextTick(() => {
this.$props.data.params[index].rows.forEach(e => {
e.values.splice(k, 1)
})
})
})
}
},
addDeviceInfo(index) {
this.$props.data.params[index].rows.forEach(e => {
e.values.push('')
})
},
addRows(index) {
this.$props.data.params[index].rows.push({
name: '',
values: Array(this.data.params[index].columns.length).fill(''),
merge: false
})
},
addParams() {
this.$props.data.params.push({title: '', rows: [], columns: []})
},
edit2(index, k, e) {
this.$set(this.$props.data.params[index].columns, k, e.target.innerText)
},
edit3(key, index, e) {
this.$props.data.params[index][key] = e.target.innerText
},
edit4(key, index, index1, e) {
this.$props.data.params[index].rows[index1][key] = e.target.innerText
},
edit5(index, index1, index2, e) {
this.$props.data.params[index].rows[index1].values[index2] = e.target.innerText
},
edit6(index, index1, e) {
this.$props.data.params[index].rows[index1].values[0] = e.target.innerText
},
mergeChange(index, i, e) {
console.log(this.$props.data)
let length = this.data.params[index].columns.length
if (e === false && this.$props.data.params[index].rows[i].values.length < length) {
this.$props.data.params[index].rows[i].values = this.$props.data.params[index].rows[i].values.concat(Array(length - this.$props.data.params[index].rows[i].values.length).fill(this.$props.data.params[index].rows[i].values[0]))
}
},
openImport(index) {
this.importingIndex = index
this.importText = ''
this.importDlg = true
},
applyImport() {
const text = (this.importText || '').trim()
if (!text) {
this.importDlg = false
return
}
const parsed = this.parseTableText(text, this.importOptions)
console.log(parsed)
this.$set(this.$props.data.params, this.importingIndex, parsed)
this.importDlg = false
},
normalizeText(text) {
return text
.replace(/\u3000/g, ' ') //
.replace(//g, ',')
.replace(//g, ':')
.replace(/[\u2013\u2014\u2015]/g, '-')
.replace(/[\t ]+\|[\t ]+/g, '|')
},
rowsToSchema(rows, {header = true, detectMerged = true, delimiter = 'auto'} = {}) {
if (!rows || !rows.length) return null
let title = ''
let columns = ['参数值']
let bodyRows = rows
if (header) {
const headerRow = rows[0]
title = headerRow[0] || ''
columns = headerRow.slice(1)
bodyRows = rows.slice(1)
if (!columns.length) columns = ['参数值']
}
const parsedRows = bodyRows.map(r => {
const name = r[0] || ''
const values = r.slice(1)
const merge = detectMerged && (values.length <= 1)
return {name, values: values.length ? values : [''], merge}
})
return {title, columns, rows: parsedRows}
},
detectDelimiter(lines) {
const hasTab = lines.filter(l => /\t/.test(l)).length
const hasPipe = lines.filter(l => /\|/.test(l)).length
const hasSemicolon = lines.filter(l => /;/.test(l)).length
const hasComma = lines.filter(l => /,/.test(l)).length
const hasColon = lines.filter(l => /[:]/.test(l)).length
const hasSpace2 = lines.filter(l => /\s{2,}/.test(l)).length
const total = lines.length
if (hasTab > total * 0.5) return 'tab'
if (hasPipe > total * 0.5) return 'pipe'
if (hasSemicolon > total * 0.5) return 'semicolon'
if (hasColon > total * 0.5) return 'colon'
if (hasSpace2 > total * 0.5) return 'space2'
if (hasComma > total * 0.5) return 'comma'
return 'space2'
},
parseTableText(text, opts = {delimiter: 'auto', header: true, detectMerged: true}) {
text = this.normalizeText(text)
// HTML
if (text.includes('<table')) {
try {
const div = document.createElement('div')
div.innerHTML = text
const table = div.querySelector('table')
if (table) {
const rows = Array.from(table.querySelectorAll('tr')).map(tr => Array.from(tr.querySelectorAll('th,td')).map(td => td.textContent.trim()))
return this.rowsToSchema(rows, {header: opts.header, detectMerged: opts.detectMerged, delimiter: 'html'})
}
} catch (e) {
}
}
const lines = text.split(/\r?\n/).map(l => l.trim()).filter(l => l.length)
// 使
const delim = opts.delimiter === 'auto' ? this.detectDelimiter(lines) : opts.delimiter
let rows
if (delim === 'tab') {
rows = lines.map(l => l.split(/\t/).map(c => c.trim()))
} else if (delim === 'comma') {
rows = lines.map(l => l.split(/,/).map(c => c.trim()))
} else if (delim === 'space2') {
rows = lines.map(l => l.split(/\s{2,}/).map(c => c.trim()))
} else if (delim === 'pipe') {
rows = lines.map(l => l.split(/\|/).map(c => c.trim()))
} else if (delim === 'semicolon') {
rows = lines.map(l => l.split(/;/).map(c => c.trim()))
} else if (delim === 'colon') {
rows = lines.map(l => {
const m = l.split(/:\s*|\s*/)
if (m.length >= 2) {
const name = m[0]
const vals = m.slice(1).join(' ').split(/[\s,,、]+/).filter(Boolean)
return [name, ...vals]
}
return [l]
})
} else {
// TAB
rows = lines.map(l => l.split(/\t|,/).map(c => c.trim()))
}
return this.rowsToSchema(rows, {header: opts.header, detectMerged: opts.detectMerged, delimiter: delim})
},
}
}
</script>
<style lang="less" scoped>
@import "~@/style.less";
.paramsTable {
position: relative;
.th {
background-color: #DBDFE7;
line-height: 3vw;
font-size: 1.3vw;
font-weight: 700;
text-align: left;
.th1 {
width: calc(30% - 2vw - 2px);
display: inline-block;
padding-left: 2vw;
border-right: 1px solid #0001
}
.th2 {
display: inline-block;
text-align: center;
position: relative;
}
}
.td {
line-height: 3vw;
font-size: 0.9vw;
text-align: left;
.td1 {
background-color: #F2F4F8;
display: inline-block;
border: 1px solid #0001;
width: calc(30% - 2vw - 2px);
padding-left: 2vw;
align-self: stretch;
}
.td2 {
background-color: #EEEEEE;
display: inline-block;
border: 1px solid #0001;
}
}
}
.del {
position: absolute;
right: 0;
top: 0;
cursor: pointer;
}
</style>

@ -44,9 +44,29 @@ export default {
methods: {
async downFile(e) {
console.log(e)
getSecureDocumentAddress({documentId: e.uuid}).then((res) => {
getSecureDocumentAddress({documentId: e.uuid}).then(async (res) => {
if (res.code === 200) {
console.log('下载地址', res.data)
let url = res.msg
let filename = url.split('/').at(-1)?.split('_')[0] + '.' + url.split('/').at(-1)?.split('.').at(-1)
try {
const response = await fetch(url, {mode: 'cors'});
if (!response.ok) throw new Error('网络错误');
const blob = await response.blob(); //
const blobUrl = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = blobUrl;
a.download = filename || url.split('/').pop();
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(blobUrl); //
this.getPasswordVisible = false
} catch (err) {
console.error('下载失败:', err);
}
} else {
this.form = {
documentId: e.uuid,

@ -0,0 +1,115 @@
<template>
<div class="param-container" style="padding: 5vw 10vw;background-color: #0001">
<h2 class="title1" style="line-height: 40px">
<span>
产品参数
</span>
</h2>
<div class="paramsTable" v-for="(param, index) in data.params" :key="index" style="position: relative;">
<div class="th">
<div class="th1">{{ param.title }}</div>
<div class="th2" v-for="(i,k) in param.columns"
:style="{width: `calc((70% - 2vw - 2px )/ ${(param.columns.length || 1 ) } )`,paddingLeft:` ${2 / ((param.columns.length || 1) )}vw`}">
{{ i }}
</div>
</div>
<div class="td">
<div v-for="(item, idx) in param.rows" :key="idx" style="position: relative;display: flex">
<div
:style="{position:'absolute',left:'100%',width: `300px`,lineHeight:'2vw',paddingLeft:` ${2 / ((param.columns.length || 1) )}vw`}"
>
</div>
<div class="td1">{{ item.name }}</div>
<div class="td2" v-if="!item.merge"
:style="{width: `calc((70% - 2vw - 4px )/ ${item.values.length || 1 } )`,paddingLeft:` ${2 / (item.values.length || 1)}vw`}"
v-for="(i,k) in item.values">{{
i
}}
</div>
<div class="td2" v-if="item.merge"
:style="{width: `calc(70% - 2vw - 4px ) `,paddingLeft:` 2vw`}"
>{{
item.values[0]
}}
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import UploadEl from "@/components/editEl/uploadEl.vue";
export default {
name: 'PlatformFeatures',
props: ['data'],
components: {UploadEl},
data() {
return {}
},
mounted() {
},
methods: {}
}
</script>
<style lang="less" scoped>
@import "~@/style.less";
.paramsTable {
position: relative;
.th {
background-color: #DBDFE7;
line-height: 3vw;
font-size: 1.3vw;
font-weight: 700;
text-align: left;
.th1 {
width: calc(30% - 2vw - 2px);
display: inline-block;
padding-left: 2vw;
border-right: 1px solid #0001
}
.th2 {
display: inline-block;
text-align: center;
position: relative;
}
}
.td {
line-height: 3vw;
font-size: 0.9vw;
text-align: left;
.td1 {
background-color: #F2F4F8;
display: inline-block;
border: 1px solid #0001;
width: calc(30% - 2vw - 2px);
padding-left: 2vw;
align-self: stretch;
}
.td2 {
background-color: #EEEEEE;
display: inline-block;
border: 1px solid #0001;
}
}
}
.del {
position: absolute;
right: 0;
top: 0;
cursor: pointer;
}
</style>

@ -46,6 +46,7 @@
<EditEl13 v-if="i.type === 13" :data="i.value"/>
<EditEl14 v-if="i.type === 14" :data="i.value"/>
<EditEl15 v-if="i.type === 15" :data="i.value"/>
<EditEl16 v-if="i.type === 16" :data="i.value"/>
</div>
<div class="addItem">
<div style="width:100%;height:100%;" @click="add">
@ -138,6 +139,11 @@
产品banner
</div>
</el-card>
<el-card shadow="hover" style="width: 200px;display: inline-block;height:70px">
<div style="width: 100%;height: 100%" @click="addItem(16)">
产品参数(多设备对比)
</div>
</el-card>
<span slot="footer" class="dialog-footer">
<el-button @click="addEl = false"> </el-button>
</span>
@ -165,6 +171,7 @@ import EditEl12 from "@/components/editEl/editEl12.vue";
import EditEl13 from "@/components/editEl/editEl13.vue";
import EditEl14 from "@/components/editEl/editEl14.vue";
import EditEl15 from "@/components/editEl/editEl15.vue";
import EditEl16 from "@/components/editEl/editEl16.vue";
export default {
components: {
@ -184,6 +191,7 @@ export default {
EditEl13,
EditEl14,
EditEl15,
EditEl16,
},
data() {
return {
@ -379,6 +387,18 @@ export default {
bannerValue: '设备介绍',
},
},
{
type: 16,
value: {
params: [
{
"title": "表名",
"columns": [],
"rows": []
}
]
},
},
],
components: [],
}
@ -404,7 +424,7 @@ export default {
this.type = this.$route.query.type
this.value = this.$route.query.id.split(',').map(v => parseFloat(v))
listHwWeb1({webCode: this.value[0], deviceId: this.value[2]}).then(e => {
listHwWeb1({webCode: this.value[0], typeId: this.value[1], deviceId: this.value[2]}).then(e => {
this.components = JSON.parse(e.rows?.[0]?.webJsonString || '[]')
})
})
@ -445,6 +465,7 @@ export default {
if (this.type === '2') {
updateHwWeb1({
webCode: this.value[0],
typeId: this.value[1],
deviceId: this.value[2],
webJsonString: JSON.stringify(this.components),
}).then(res => {
@ -469,7 +490,7 @@ export default {
this.components = JSON.parse(res?.data?.webJsonString || '[]')
})
} else {
listHwWeb1({webCode: e[0], deviceId: e[2]}).then(e => {
listHwWeb1({webCode: e[0], typeId: e[1], deviceId: e[2]}).then(e => {
this.components = JSON.parse(e.rows?.[0]?.webJsonString || '[]')
})
}

@ -1,8 +1,8 @@
<template>
<div>
<el-carousel trigger="click" style="height:100%" :indicator-position="indicatorPosition ||''">
<el-carousel-item v-for="item in bannerList" :key="item" >
<div style="position: relative">
<el-carousel-item v-for="item in bannerList" :key="JSON.stringify(item)">
<div style="position: relative;width: 100%;height: 100%;">
<el-image
style="width: 100%;height: 100%"
:src="item.portalConfigPic"

@ -26,6 +26,7 @@
<EditEl13 v-if="i.type === 13" :data="i.value"/>
<EditEl14 v-if="i.type === 14" :data="i.value"/>
<EditEl15 v-if="i.type === 15" :data="i.value"/>
<EditEl16 v-if="i.type === 16" :data="i.value"/>
</div>
</div>
@ -50,6 +51,7 @@ import EditEl12 from "@/components/el/el12.vue";
import EditEl13 from "@/components/el/el13.vue";
import EditEl14 from "@/components/el/el14.vue";
import EditEl15 from "@/components/el/el15.vue";
import EditEl16 from "@/components/el/el16.vue";
import Copyright from '@/components/copyright'
import ContactUs from "@/views/index/contactUs.vue";
@ -82,6 +84,7 @@ export default {
EditEl13,
EditEl14,
EditEl15,
EditEl16,
},
data() {
return {

@ -26,6 +26,7 @@
<El13 v-if="i.type === 13" :data="i.value"/>
<El14 v-if="i.type === 14" :data="i.value"/>
<El15 v-if="i.type === 15" :data="i.value"/>
<El16 v-if="i.type === 16" :data="i.value"/>
</div>
</div>
@ -50,6 +51,7 @@ import El12 from "@/components/el/el12.vue";
import El13 from "@/components/el/el13.vue";
import El14 from "@/components/el/el14.vue";
import El15 from "@/components/el/el15.vue";
import El16 from "@/components/el/el16.vue";
import Copyright from '@/components/copyright'
import ContactUs from "@/views/index/contactUs.vue";
import Carousel from "@/components/el/carousel.vue";
@ -81,6 +83,7 @@ export default {
El13,
El14,
El15,
El16,
},
data() {
return {
@ -92,13 +95,21 @@ export default {
},
watch: {
'$route'() {
listHwWeb1({webCode: this.$route.query.type, deviceId: this.$route.query.id}).then(e => {
listHwWeb1({
webCode: this.$route.query.type,
typeId: this.$route.query.typeId,
deviceId: this.$route.query.id
}).then(e => {
this.components = JSON.parse(e.rows?.[0]?.webJsonString || '[]')
})
}
},
mounted() {
listHwWeb1({webCode: this.$route.query.type, deviceId: this.$route.query.id}).then(e => {
listHwWeb1({
webCode: this.$route.query.type,
typeId: this.$route.query.typeId,
deviceId: this.$route.query.id
}).then(e => {
this.components = JSON.parse(e.rows?.[0]?.webJsonString || '[]')
})
},

@ -13,7 +13,7 @@
<div class="tabs">
<el-tabs v-model="activeName">
<el-tab-pane :name="`${k}`" v-for="(i,k) in data.list">
<template slot="label">
<template slot="label">
<span>
{{ i.name }}
</span>
@ -32,19 +32,19 @@
<!-- 按钮 -->
<div class="btn-wrap" v-if="ii.id">
<el-button class="more-btn" @click.stop="toDetail(ii.id)">
<el-button class="more-btn" @click.stop="toDetail(ii.id,i.id)">
了解更多
<i class="el-icon-arrow-right icon"></i>
</el-button>
</div>
<!-- 图片 -->
<div class="img-wrap">
<div class="img-wrap" style="height: calc(30vw - 31px - 40px - 26px - 3.2vw - 42px - 8px)">
<el-image
class="image"
:src="ii.icon"
fit="cover"
style="width: 100%; max-height: 20vw;border-radius: 12px"
fit="contain"
style="width: 100%; height: 100%;border-radius: 12px"
/>
</div>
</el-card>
@ -67,10 +67,10 @@ export default {
},
methods:{
toDetail(id) {
console.log(123123)
toDetail(id, typeId) {
console.log(typeId)
if (id) {
this.$router.push(`/productCenter/detail?type=${this.$props.data.id}&id=${id}`)
this.$router.push(`/productCenter/detail?type=${this.$props.data.id}&typeId=${typeId}&id=${id}`)
}
},
}

Loading…
Cancel
Save