全栈小学生 fa49aade7a up admin
2025-06-11 09:17:09 +08:00

832 lines
33 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="main-container flex-1">
<el-header class="flex items-center h-[60px] bg-primary px-[20px]">
<div class="text-white cursor-pointer flex items-center" @click="goBack">
<el-icon size="14">
<ArrowLeft />
</el-icon>
<span class="pl-[5px] text-[14px]">{{ t('back') }}</span>
</div>
<div class="text-white ml-[10px] mr-[20px] flex items-center">
<span class="mr-[5px] text-[rgba(255,255,255,.5)]"></span>
<span class="mr-[5px] text-[14px]">{{ t('decorating') }}{{ diyStore.typeName }}</span>
<!--<el-icon class="font-bold"><EditPen /></el-icon>-->
</div>
<div v-if="diyStore.type && diyStore.type != 'DIY_PAGE'" class="flex items-center">
<span class="text-white mr-[10px] text-base">{{ t('templatePagePlaceholder') }}</span>
<div class="w-[180px]">
<el-select size="small" v-model="template" :placeholder="t('templatePagePlaceholder')" @change="changeTemplatePage">
<el-option :label="t('templatePageEmpty')" value="" />
<el-option v-for="(item, key) in templatePages" :label="item.title" :value="key" :key="key"/>
</el-select>
</div>
</div>
<div class="flex-1"></div>
<el-button @click="preview()">{{ t('preview') }}</el-button>
<el-button @click="save()">{{ t('save') }}</el-button>
</el-header>
<div class="full-container flex flex-row flex-1 bg-page">
<div class="component-list w-[290px]">
<!-- 组件列表区域 -->
<el-scrollbar class="px-[10px]">
<el-collapse v-model="activeNames" @change="handleChange">
<el-collapse-item v-for="(item, key) in component" :key="key" :title="item.title" :name="key">
<ul class="flex flex-row flex-wrap">
<li v-for="(compItem, compKey) in item.list" :key="compKey" class="w-2/6 text-center cursor-pointer h-[65px]" :title="compItem.title" @click="diyStore.addComponent(compKey, compItem)">
<icon v-if="compItem.icon" :name="compItem.icon" size="20px" class="inline-block mt-[3px]" />
<icon v-else name="iconfont iconkaifazujian" size="20px" class="inline-block mt-[3px]" />
<span class="block text-[12px] truncate">{{ compItem.title }}</span>
</li>
</ul>
</el-collapse-item>
</el-collapse>
</el-scrollbar>
</div>
<div class="preview-wrap flex-1 relative mt-[20px]">
<el-scrollbar>
<el-button class="page-btn absolute right-[20px]" @click="diyStore.changeCurrentIndex(-99)">{{ t('pageSet') }}</el-button>
<div class="diy-view-wrap w-[375px] shadow-lg mx-auto">
<div class="preview-head bg-no-repeat bg-center bg-cover cursor-pointer h-[64px]" :class="[diyStore.global.topStatusBar.style]" :style="{backgroundColor :diyStore.global.topStatusBar.bgColor}" @click="diyStore.changeCurrentIndex(-99)">
<div v-if="diyStore.global.topStatusBar.style == 'style-1' && diyStore.global.topStatusBar.isShow" class="content-wrap">
<div class="title-wrap" :style="{ fontSize: '14px', color: diyStore.global.topStatusBar.textColor, textAlign: diyStore.global.topStatusBar.textAlign }">
{{ diyStore.global.title }}
</div>
</div>
<div v-if="diyStore.global.topStatusBar.style == 'style-2' && diyStore.global.topStatusBar.isShow" class="content-wrap">
<div class="title-wrap" :style="{ color: diyStore.global.topStatusBar.textColor }">
<div class="h-[28px] max-w-[150px] mr-[8px]" v-if="diyStore.global.topStatusBar.imgUrl">
<img class="max-w-[100%] max-h-[100%]" :src="img(diyStore.global.topStatusBar.imgUrl)" mode="heightFix" />
</div>
<div :style="{ color: diyStore.global.topStatusBar.textColor }">{{ diyStore.global.title }}</div>
</div>
</div>
<div v-if="diyStore.global.topStatusBar.style == 'style-3' && diyStore.global.topStatusBar.isShow" class="content-wrap">
<div class="title-wrap" v-if="diyStore.global.topStatusBar.imgUrl">
<img class="max-w-[100%] max-h-[100%]" :src="img(diyStore.global.topStatusBar.imgUrl)" />
</div>
<div class="search">
<span class="nc-iconfont nc-icon-sousuo-duanV6xx1 !text-[12px] absolute left-[10px]"></span>
<span class="text-[12px]">{{diyStore.global.topStatusBar.inputPlaceholder}}</span>
</div>
</div>
<div v-if="diyStore.global.topStatusBar.style == 'style-4' && diyStore.global.topStatusBar.isShow" class="content-wrap">
<span class="iconfont iconxiazai19 !text-[14px]" :style="{ color: diyStore.global.topStatusBar.textColor }"></span>
<div class="title-wrap" :style="{ color: diyStore.global.topStatusBar.textColor }">我的位置</div>
<span class="iconfont iconxiangyoujiantou !text-[12px]" :style="{ color: diyStore.global.topStatusBar.textColor }"></span>
</div>
</div>
<div class="preview-block relative">
<ul class="quick-action absolute text-center -right-[70px] top-[20px] w-[42px] rounded shadow-md">
<el-tooltip effect="light" :content="t('moveUpComponent')" placement="right">
<icon name="iconfont iconjiantoushang" size="20px" class="block cursor-pointer leading-[40px]" @click="diyStore.moveUpComponent" />
</el-tooltip>
<el-tooltip effect="light" :content="t('moveDownComponent')" placement="right">
<icon name="iconfont iconjiantouxia" size="20px" class="block cursor-pointer leading-[40px]" @click="diyStore.moveDownComponent" />
</el-tooltip>
<el-tooltip effect="light" :content="t('copyComponent')" placement="right">
<icon name="iconfont iconcopy-line" size="20px" class="block cursor-pointer leading-[40px]" @click="diyStore.copyComponent" />
</el-tooltip>
<el-tooltip effect="light" :content="t('delComponent')" placement="right">
<icon name="iconfont icondelete-line" size="20px" class="block cursor-pointer leading-[40px]" @click="diyStore.delComponent" />
</el-tooltip>
<el-tooltip effect="light" :content="t('resetComponent')" placement="right">
<icon name="iconfont iconloader-line" size="20px" class="block cursor-pointer leading-[40px]" @click="diyStore.resetComponent" />
</el-tooltip>
</ul>
<!-- 组件预览渲染区域 -->
<iframe id="previewIframe" v-show="loadingIframe" :src="wapPreview" frameborder="0" class="preview-iframe w-[375px]"></iframe>
<div v-show="loadingDev" class="preview-iframe w-[375px] pt-[20px] px-[20px]">
<div class="font-bold text-xl mb-[40px]">{{ t('developTitle') }}</div>
<div class="mb-[20px] flex flex-col">
<text class="mb-[10px]">{{ t('wapDomain') }}</text>
<el-input v-model.trim="wapDomain" :placeholder="t('wapDomainPlaceholder')" clearable />
</div>
<el-button type="primary" @click="saveWapDomain">{{ t('confirm') }}</el-button>
<el-button type="primary" @click="settingTips()" plain>{{ t('settingTips') }}</el-button>
</div>
</div>
</div>
</el-scrollbar>
</div>
<div class="edit-attribute-wrap w-[400px]">
<!-- 编辑组件属性区域 -->
<el-scrollbar>
<el-card class="box-card" shadow="never">
<template #header>
<div class="card-header flex justify-between items-center">
<span class="title flex-1">{{ diyStore.currentIndex == -99 ? t('pageSet') : diyStore.editComponent.componentTitle }}</span>
<div class="tab-wrap flex rounded-[50px] bg-gray-100 text-[14px]" v-if="diyStore.currentComponent">
<span class="cursor-pointer rounded-[50px] py-[5px] px-[15px]" :class="{ 'bg-primary text-white': diyStore.editTab == 'content' }" @click="diyStore.editTab = 'content'">{{ t('tabEditContent') }}</span>
<span class="cursor-pointer rounded-[50px] py-[5px] px-[15px]" :class="{ 'bg-primary text-white': diyStore.editTab == 'style' }" @click="diyStore.editTab = 'style'">{{ t('tabEditStyle') }}</span>
</div>
</div>
</template>
<div class="edit-component-wrap">
<component v-if="diyStore.currentComponent" :is="modules[diyStore.currentComponent]" :key="diyStore.currentIndex" :value="diyStore.value[diyStore.currentIndex]">
<template #style>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('componentStyleTitle') }}</h3>
<el-form label-width="90px" class="px-[10px]">
<template v-if="diyStore.editComponent.ignore.indexOf('pageBgColor') == -1">
<el-form-item :label="t('bottomBgColor')">
<el-color-picker v-model="diyStore.editComponent.pageStartBgColor" show-alpha :predefine="diyStore.predefineColors" />
<icon name="iconfont iconmap-connect" size="20px" class="block !text-gray-400 mx-[5px]"/>
<el-color-picker v-model="diyStore.editComponent.pageEndBgColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<div class="text-sm text-gray-400 ml-[90px] mb-[10px]">{{ t('bottomBgTips') }}</div>
</template>
<el-form-item :label="t('bgGradientAngle')" v-if="diyStore.editComponent.ignore.indexOf('pageBgColor') == -1">
<el-radio-group v-model="diyStore.editComponent.pageGradientAngle">
<el-radio label="to bottom">{{ t('topToBottom') }}</el-radio>
<el-radio label="to right">{{ t('leftToRight') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('componentBgUrl')" v-if="diyStore.editComponent.ignore.indexOf('componentBgUrl') == -1">
<upload-image v-model="diyStore.editComponent.componentBgUrl" :limit="1"/>
</el-form-item>
<el-form-item :label="t('componentBgAlpha')" v-if="diyStore.editComponent.ignore.indexOf('componentBgUrl') == -1 && diyStore.editComponent.componentBgUrl">
<el-slider v-model="diyStore.editComponent.componentBgAlpha" show-input size="small" :min="0" :max="10" class="ml-[10px] diy-nav-slider" />
</el-form-item>
<el-form-item :label="t('componentBgColor')" v-if="diyStore.editComponent.ignore.indexOf('componentBgColor') == -1">
<el-color-picker v-model="diyStore.editComponent.componentStartBgColor" show-alpha :predefine="diyStore.predefineColors" />
<icon name="iconfont iconmap-connect" size="20px" class="block !text-gray-400 mx-[5px]"/>
<el-color-picker v-model="diyStore.editComponent.componentEndBgColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('bgGradientAngle')" v-if="diyStore.editComponent.ignore.indexOf('componentBgColor') == -1">
<el-radio-group v-model="diyStore.editComponent.componentGradientAngle">
<el-radio label="to bottom">{{ t('topToBottom') }}</el-radio>
<el-radio label="to right">{{ t('leftToRight') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('marginTop')" v-if="diyStore.editComponent.ignore.indexOf('marginTop') == -1">
<el-slider v-model="diyStore.editComponent.margin.top" show-input size="small" :min="-100" class="ml-[10px] diy-nav-slider" />
</el-form-item>
<el-form-item :label="t('marginBottom')" v-if="diyStore.editComponent.ignore.indexOf('marginBottom') == -1">
<el-slider v-model="diyStore.editComponent.margin.bottom" show-input size="small" class="ml-[10px] diy-nav-slider" />
</el-form-item>
<el-form-item :label="t('marginBoth')" v-if="diyStore.editComponent.ignore.indexOf('marginBoth') == -1">
<el-slider v-model="diyStore.editComponent.margin.both" show-input size="small" class="ml-[10px] diy-nav-slider" />
</el-form-item>
<el-form-item :label="t('topRounded')" v-if="diyStore.editComponent.ignore.indexOf('topRounded') == -1">
<el-slider v-model="diyStore.editComponent.topRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="100" />
</el-form-item>
<el-form-item :label="t('bottomRounded')" v-if="diyStore.editComponent.ignore.indexOf('bottomRounded') == -1">
<el-slider v-model="diyStore.editComponent.bottomRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="100" />
</el-form-item>
</el-form>
</div>
</template>
</component>
</div>
</el-card>
</el-scrollbar>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, toRaw, watch, inject } from 'vue'
import { t } from '@/lang'
import { ArrowLeft } from "@element-plus/icons-vue"
import { img } from '@/utils/common'
import { getDiyTemplatePages, addDiyPage, editDiyPage, initPage } from '@/app/api/diy'
import { useRoute, useRouter } from 'vue-router'
import { cloneDeep } from 'lodash-es'
import { ElMessage, ElMessageBox } from 'element-plus'
import useDiyStore from '@/stores/modules/diy'
import storage from '@/utils/storage'
const setLayout = inject('setLayout')
setLayout('decorate')
const diyStore = useDiyStore()
const route = useRoute()
const router = useRouter()
route.query.id = route.query.id || 0
route.query.name = route.query.name || ''
route.query.url = route.query.url || '' // 页面路径
route.query.type = route.query.type || '' // 页面模板,新页面传入
route.query.title = route.query.title || ''
route.query.back = route.query.back || '/admin/diy/list'
const backPath = route.query.back
const template = ref('')
const oldTemplate = ref('')
const wapUrl = ref('')
const wapDomain = ref('')
const wapPreview = ref('')
const loadingIframe = ref(false) // 加载iframe
const loadingDev = ref(false) // 加载开发环境配置
const timeIframe = ref(0) // iframe打开时间
const difference = ref(0) // 检测页面加载差异小于1000毫秒则配置wap端域名
const component = ref([])
const componentType: string[] = reactive([])
const page = ref('')
const activeNames = ref(componentType)
const handleChange = (val: string[]) => {
}
// 初始化原数据
const originData = reactive({
id: diyStore.id,
name: diyStore.name,
pageTitle: diyStore.pageTitle,
title: diyStore.global.title,
value: JSON.stringify({
global: toRaw(diyStore.global),
value: toRaw(diyStore.value)
})
})
// 返回上一页
const isChange = ref(true) // 数据是否发生变化true没变化false变化了
const goBack = () => {
if (isChange.value) {
location.href = `${location.origin}${backPath}`
router.push(backPath)
} else {
// 数据发生变化,弹框提示:确定离开此页面
ElMessageBox.confirm(
t('leavePageTitleTips'),
t('leavePageContentTips'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
autofocus: false
}
).then(() => {
location.href = `${location.origin}${backPath}`
}).catch(() => {
})
}
}
// 动态加载后台自定义组件编辑
const modulesFiles = import.meta.glob('./components/*.vue', { eager: true })
const addonModulesFiles = import.meta.glob('@/addon/**/views/diy/components/*.vue', { eager: true })
addonModulesFiles && Object.assign(modulesFiles, addonModulesFiles)
const modules = {}
for (const [key, value] of Object.entries(modulesFiles)) {
const moduleName = key.split('/').pop()
const name = moduleName.split('.')[0]
modules[name] = value.default
}
// 获取模板页面列表
const templatePages: any = reactive({})
const loadDiyTemplatePages = (type:any)=>{
getDiyTemplatePages({
type,
mode: 'diy'
}).then(res => {
for (const key in res.data) {
templatePages[key] = res.data[key];
}
});
}
// 全局监听自定义数据变化
watch(
() => template.value,
(newValue, oldValue) => {
oldTemplate.value = oldValue;
}
)
// 切换模板页面
const changeTemplatePage = (value: any) => {
// 存在数据则弹框提示确认
if (diyStore.value.length) {
ElMessageBox.confirm(t('changeTemplatePageTips'), t('warning'), {
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}).then(() => {
diyStore.changeCurrentIndex(-99)
diyStore.init() // 清空
if (value) {
let data = cloneDeep(templatePages[value].data);
diyStore.global = data.global;
if (data.value.length) {
diyStore.value = data.value
}
} else {
if (route.query.title) diyStore.global.title = diyStore.typeName
}
}).catch(() => {
// 还原
template.value = oldTemplate.value
});
} else {
diyStore.init() // 清空
if (value) {
let data = cloneDeep(templatePages[value].data)
diyStore.global = data.global
if (data.value.length) {
diyStore.value = data.value
}
} else {
if (route.query.title) diyStore.global.title = diyStore.typeName
}
}
};
// 全局监听自定义数据变化
watch(
() => diyStore,
(newValue, oldValue) => {
const data = {
id: newValue.id,
name: newValue.name,
pageTitle: newValue.pageTitle,
title: newValue.global.title,
value: JSON.stringify({
global: toRaw(newValue.global),
value: toRaw(newValue.value)
})
}
diyStore.postMessage()
isChange.value = JSON.stringify(data) == JSON.stringify(originData)
},
{ deep: true }
)
// 根据当前页面路由查询页面初始化数据
initPage({
id: route.query.id,
name: route.query.name,
url: route.query.url,
type: route.query.type,
title: route.query.title
}).then(async (res) => {
const data = res.data
diyStore.init() // 初始化清空数据
diyStore.id = data.id || 0
diyStore.name = data.name
diyStore.pageTitle = data.page_title
diyStore.type = data.type
diyStore.typeName = data.type_name
diyStore.templateName = data.template
template.value = data.template;
diyStore.isDefault = data.is_default
diyStore.pageMode = data.mode
if (data.value) {
const sources = JSON.parse(data.value)
diyStore.global = sources.global
if (sources.value.length) {
diyStore.value = sources.value
// diyStore.changeCurrentIndex(0,diyStore.value[0]);
}
} else {
diyStore.global.title = data.title
}
// 初始化原数据
originData.id = diyStore.id
originData.name = diyStore.name
originData.pageTitle = diyStore.pageTitle
originData.title = diyStore.global.title
originData.value = JSON.stringify({
global: toRaw(diyStore.global),
value: toRaw(diyStore.value)
})
component.value = data.component
for (const type in component.value) {
componentType.push(type)
for (const key in component.value[type].list) {
const com = cloneDeep(component.value[type].list[key])
com.id = diyStore.generateRandom()
com.componentName = key
com.componentTitle = com.title
Object.assign(com, com.value)
delete com.name
delete com.title
delete com.value
delete com.type
delete com.icon
diyStore.components.push(com)
}
}
loadDiyTemplatePages(data.type)
// 加载预览
wapDomain.value = data.domain_url.wap_domain
wapUrl.value = data.domain_url.wap_url
page.value = data.page
let repeat = true; // 防重复执行
// 开发模式下执行
if (import.meta.env.MODE == 'development') {
// env文件配置过wap域名
if (wapDomain.value) {
wapUrl.value = wapDomain.value + '/wap'
repeat = false
setDomain()
}
let wap_domain_storage = storage.get('wap_domain')
if (wap_domain_storage) {
wapUrl.value = wap_domain_storage
repeat = false
setDomain()
}
}
if(repeat) {
setDomain()
}
})
const uniAppLoadStatus = ref(false) // uni-app 加载状态true加载完成false未完成
// 监听组件数据 uni-app端
window.addEventListener('message', (event) => {
try {
let data = {
type: ''
};
if(typeof event.data == 'string') {
data = JSON.parse(event.data)
}else if(typeof event.data == 'object') {
data = event.data
}
if (!data.type) return
switch (data.type) {
case 'appOnLaunch':
case 'appOnReady':
// uni-app 加载完成
loadingDev.value = false
loadingIframe.value = true
let loadTime = new Date().getTime()
difference.value = loadTime - timeIframe.value
uniAppLoadStatus.value = true // 加载完成
break
case 'init':
// 初始化与uniapp建立连接传输数据
diyStore.load = true
diyStore.postMessage()
break
case 'change':
// 切换
diyStore.changeCurrentIndex(data.index, data.component)
break
case 'data':
// 传数据
diyStore.changeCurrentIndex(data.index, data.component)
diyStore.global = data.global
diyStore.value = data.value
break
}
} catch (e) {
console.log('diy edit 后台接受数据错误', e)
}
}, false)
const saveWapDomain = () => {
if (wapDomain.value.trim().length == 0) {
ElMessage({
type: 'warning',
message: `${t('wapDomainPlaceholder')}`
})
return
}
wapUrl.value = wapDomain.value + '/wap'
setDomain()
storage.set({ key: 'wap_domain', data: wapUrl.value })
loadingIframe.value = true
loadingDev.value = false
}
const setDomain = () => {
wapPreview.value = `${wapUrl.value}${page.value}?mode=decorate` // 模式decorate 装修 访问预览页面
const send = ()=>{
timeIframe.value = new Date().getTime()
postMessage()
}
// 同步发送一次消息
send()
// 如果同步发送消息的 uni-app没有接收到回应则定时发送消息
let sendCount = 0;
let timeInterVal = setInterval(()=>{
// 接收 uni-app 发送的消息 或者 发送50次后未响应则停止发送
if(uniAppLoadStatus.value || sendCount >= 50){
clearInterval(timeInterVal)
return
}
send()
sendCount++;
},200)
// 如果10秒内加载不出来则需要配置域名
setTimeout(() => {
if (difference.value == 0) initLoad()
}, 1000 * 10)
}
// 将数据发送到uniapp
const postMessage = () => {
const data = JSON.stringify({
type: 'appOnReady',
message: '加载完成'
})
if (window.previewIframe) window.previewIframe.contentWindow.postMessage(data, '*')
}
// 初始化加载状态
const initLoad = () => {
loadingDev.value = true
loadingIframe.value = false
wapPreview.value = ''
}
const isRepeat = ref(false)
const save = (callback: any) => {
if (!diyStore.verify()) {
return
}
if (isRepeat.value) return
isRepeat.value = true
diyStore.templateName = template.value;
let data = {
id: diyStore.id,
name: diyStore.name,
page_title: diyStore.pageTitle,
title: diyStore.global.title,
type: diyStore.type,
template: diyStore.templateName,
is_default: diyStore.isDefault,
is_change: isChange.value ? 0 : 1,
value: JSON.stringify({
global: toRaw(diyStore.global),
value: toRaw(diyStore.value)
})
}
const api = diyStore.id ? editDiyPage : addDiyPage
api(data).then((res: any) => {
isRepeat.value = false
if (res.code == 1) {
if (diyStore.id) {
isRepeat.value = false // 不刷新
} else {
location.href = `${location.origin}${backPath}`;
}
if (callback) callback(res.data.id)
}
}).catch(() => {
isRepeat.value = false
})
}
// 预览
const preview = () => {
save((id: number) => {
id = diyStore.id || id
const url = router.resolve({
path: '/admin/preview/wap',
query: {
page: page.value + '?id=' + id
}
})
window.open(url.href)
})
}
const settingTips = () => {
window.open('https://www.kancloud.cn/niucloud/niucloud-admin-develop/3213393')
}
</script>
<style lang="scss">
.el-collapse-item__wrap {
border-bottom: none;
}
.el-collapse-item__content {
padding-bottom: 0;
}
.el-collapse-item__header {
font-size: var(--el-font-size-base);
}
.display-block {
.el-form-item__content {
display: block;
}
}
.edit-component-wrap {
.content-wrap,
.style-wrap {
.edit-attr-item-wrap {
border-top: 2px solid var(--el-color-info-light-8);
padding-top: 20px;
&:first-of-type {
border-top: none;
padding-top: 0;
}
}
}
}
.diy-nav-slider {
.el-slider__input {
width: 100px;
}
}
</style>
<style lang="scss" scoped>
.full-container {
height: calc(100vh - 60px);
}
.preview-iframe {
height: calc(100vh - 160px);
}
.component-list {
background: var(--el-bg-color);
}
.component-list ul li {
&:not(.disabled):hover {
color: var(--el-color-primary);
background: var(--el-color-primary-light-9);
}
}
.diy-view-wrap {
background: var(--el-bg-color-page);
}
.diy-view-wrap .preview-head {
background-image: url(@/app/assets/images/diy_preview_head.png);
background-color: var(--el-bg-color);
}
.quick-action {
background: var(--el-bg-color);
}
.edit-attribute-wrap {
background: var(--el-bg-color);
}
.edit-attribute-wrap .box-card {
border: none;
}
.diy-view-wrap .preview-head {
padding: 28px 15px 0;
.content-wrap {
height: 30px;
}
&.style-1 {
.content-wrap {
.title-wrap {
height: 30px;
line-height: 30px;
}
}
}
&.style-2 {
.content-wrap {
.title-wrap {
display: flex;
align-items: center;
> div {
height: 30px;
line-height: 30px;
max-width: 150px;
font-size: 14px;
&:last-child {
overflow: hidden; //超出的文本隐藏
text-overflow: ellipsis; //用省略号显示
white-space: nowrap; //不换行
flex: 1;
max-width: 200px;
}
}
}
}
}
&.style-3 {
.content-wrap {
display: flex;
align-items: center;
.title-wrap {
height: 30px;
max-width: 85px;
margin-right: 5px;
display: flex;
align-items: center;
justify-content: center;
}
.search {
flex: 1;
padding-right: 10px;
padding-left: 31px;
position: relative;
background-color: #fff;
text-align: left;
border-radius: 30px;
height: 30px;
line-height: 30px;
border: 1px solid #eeeeee;
color: rgb(102, 102, 102);
display: flex;
align-items: center;
margin-right: 105px;
overflow: hidden;
box-sizing: border-box;
span {
overflow: hidden; //超出的文本隐藏
text-overflow: ellipsis; //用省略号显示
white-space: nowrap; //不换行
}
.iconfont {
color: #909399;
font-size: 16px;
margin-right: 5px;
}
}
}
}
&.style-4 {
.content-wrap {
display: flex;
align-items: center;
.title-wrap {
flex: none;
margin: 0 5px;
max-width: 180px;
font-size: 14px;
}
}
}
}
</style>