wip: 客户评级弹窗化/公司信息精简/参数中文化/品牌站点同步(基于 927592b)
This commit is contained in:
1 parent
927592b472
commit
27533eb465
43 files changed
+2959
-434
No files matched your search
@@ -1,42 +1,85 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, reactive, ref } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
|
||||
import type { UploadRawFile } from 'element-plus'
|
||||
|
||||
import * as settingsApi from '@/api/settings'
|
||||
import type { CompanySettings } from '@/api/settings'
|
||||
import { useSettingsStore } from '@/store/settings'
|
||||
import { handledError } from '@/utils/error'
|
||||
|
||||
/**
|
||||
* 公司信息设置(2026-10-15 补充需求:公司名称、logo)。
|
||||
* 公司信息与站点信息设置(2026-10-20 定稿)。
|
||||
*
|
||||
* 签名元素是右侧「登录页实景预览」:品牌名与 logo 改动即时反映在一张
|
||||
* 缩比登录卡上 —— 所见即所得,避免「保存后去登录页核对」的往返。
|
||||
* logo 校验(png/jpg ≤2MB)与后端三重校验同口径,前端先拦一道。
|
||||
* 页面按「左侧设置 → 右侧预览」成对排列,四张卡两行:
|
||||
* - 第一行:登录页信息(名称/标语/页脚/备案号/logo)→ 登录页预览
|
||||
* - 第二行:网页站点信息(站点标题/站点图标)→ 浏览器标签页预览
|
||||
*
|
||||
* 进入页面即加载当前设置(设置页即所见即所得,无需手动刷新)。
|
||||
* 两张设置卡**各自独立保存**(后端为部分更新语义):
|
||||
* 改登录页信息不必触碰站点标题,反之亦然;保存后把回包写入 settings store,
|
||||
* 侧边栏/浏览器标题/favicon/登录页立即同步(不重新请求 public 端点 ——
|
||||
* 匿名端点带 300s 浏览器缓存)。
|
||||
*/
|
||||
const formRef = ref<FormInstance>()
|
||||
const submitting = ref(false)
|
||||
const route = useRoute()
|
||||
const settingsStore = useSettingsStore()
|
||||
|
||||
const form = reactive({
|
||||
// ==================== 登录页信息 ====================
|
||||
|
||||
const loginFormRef = ref<FormInstance>()
|
||||
const savingLogin = ref(false)
|
||||
|
||||
const loginForm = reactive({
|
||||
company_name: '',
|
||||
brand_slogan: '',
|
||||
footer_text: '',
|
||||
icp_no: '',
|
||||
})
|
||||
|
||||
const rules: FormRules = {
|
||||
const loginRules: FormRules = {
|
||||
company_name: [
|
||||
{ required: true, message: '请输入公司名称', trigger: 'blur' },
|
||||
{ max: 64, message: '公司名称最长 64 个字符', trigger: 'blur' },
|
||||
],
|
||||
brand_slogan: [{ max: 64, message: '标语最长 64 个字符', trigger: 'blur' }],
|
||||
footer_text: [{ max: 64, message: '页脚文本最长 64 个字符', trigger: 'blur' }],
|
||||
icp_no: [{ max: 32, message: '备案号最长 32 个字符', trigger: 'blur' }],
|
||||
}
|
||||
|
||||
// ==================== 网页站点信息 ====================
|
||||
|
||||
const siteFormRef = ref<FormInstance>()
|
||||
const savingSite = ref(false)
|
||||
|
||||
const siteForm = reactive({
|
||||
site_title: '',
|
||||
})
|
||||
|
||||
const siteRules: FormRules = {
|
||||
site_title: [{ max: 64, message: '站点标题最长 64 个字符', trigger: 'blur' }],
|
||||
}
|
||||
|
||||
// ==================== 图片资源 ====================
|
||||
|
||||
const logoUrl = ref<string | null>(null)
|
||||
const uploading = ref(false)
|
||||
const faviconUrl = ref<string | null>(null)
|
||||
const uploadingLogo = ref(false)
|
||||
const uploadingFavicon = ref(false)
|
||||
|
||||
const MAX_IMAGE_BYTES = 2 * 1024 * 1024
|
||||
|
||||
/**
|
||||
* 生效的站点图标:自定义图标 → 公司 logo → 内置 SVG。
|
||||
* 与 settings store 同口径 —— 未单独上传图标时,换 logo 标签页图标跟着变。
|
||||
*/
|
||||
const effectiveFavicon = computed(() => faviconUrl.value ?? logoUrl.value ?? '/favicon.svg')
|
||||
|
||||
async function loadSettings(): Promise<void> {
|
||||
try {
|
||||
const data = await settingsApi.getSettings()
|
||||
form.company_name = data.company_name
|
||||
logoUrl.value = data.logo_url
|
||||
fillAll(data)
|
||||
// 管理端打开设置页即用最新值校准全局品牌/站点信息
|
||||
settingsStore.apply(data)
|
||||
} catch (error) {
|
||||
handledError(error)
|
||||
}
|
||||
@@ -44,118 +87,309 @@ async function loadSettings(): Promise<void> {
|
||||
|
||||
loadSettings()
|
||||
|
||||
async function handleSubmit(): Promise<void> {
|
||||
const valid = await formRef.value?.validate().catch(() => false)
|
||||
function fillAll(data: CompanySettings): void {
|
||||
loginForm.company_name = data.company_name
|
||||
loginForm.brand_slogan = data.brand_slogan ?? ''
|
||||
loginForm.footer_text = data.footer_text ?? ''
|
||||
loginForm.icp_no = data.icp_no ?? ''
|
||||
siteForm.site_title = data.site_title ?? ''
|
||||
logoUrl.value = data.logo_url
|
||||
faviconUrl.value = data.favicon_url
|
||||
}
|
||||
|
||||
/** 保存登录页信息(只提交本卡字段,不触碰站点标题) */
|
||||
async function saveLoginInfo(): Promise<void> {
|
||||
const valid = await loginFormRef.value?.validate().catch(() => false)
|
||||
if (!valid) return
|
||||
submitting.value = true
|
||||
savingLogin.value = true
|
||||
try {
|
||||
const data = await settingsApi.updateSettings(form.company_name.trim())
|
||||
form.company_name = data.company_name
|
||||
ElMessage.success('已保存')
|
||||
} catch (error) {
|
||||
handledError(error)
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function beforeUpload(file: UploadRawFile): boolean {
|
||||
const okType = ['image/png', 'image/jpeg'].includes(file.type)
|
||||
if (!okType) {
|
||||
ElMessage.warning('logo 仅支持 png / jpg 格式')
|
||||
return false
|
||||
}
|
||||
if (file.size > 2 * 1024 * 1024) {
|
||||
ElMessage.warning('logo 文件不能超过 2MB')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
async function handleUpload(options: { file: UploadRawFile }): Promise<void> {
|
||||
uploading.value = true
|
||||
try {
|
||||
const data = await settingsApi.uploadLogo(options.file)
|
||||
const data = await settingsApi.updateSettings({
|
||||
company_name: loginForm.company_name.trim(),
|
||||
brand_slogan: loginForm.brand_slogan.trim(),
|
||||
footer_text: loginForm.footer_text.trim(),
|
||||
icp_no: loginForm.icp_no.trim(),
|
||||
})
|
||||
// 只回填本卡字段:另一张卡未保存的编辑不被覆盖
|
||||
loginForm.company_name = data.company_name
|
||||
loginForm.brand_slogan = data.brand_slogan ?? ''
|
||||
loginForm.footer_text = data.footer_text ?? ''
|
||||
loginForm.icp_no = data.icp_no ?? ''
|
||||
logoUrl.value = data.logo_url
|
||||
ElMessage.success('logo 已更新')
|
||||
faviconUrl.value = data.favicon_url
|
||||
settingsStore.apply(data)
|
||||
ElMessage.success('登录页信息已保存')
|
||||
} catch (error) {
|
||||
handledError(error)
|
||||
} finally {
|
||||
uploading.value = false
|
||||
savingLogin.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 预览品牌名:跟随输入(空时回退占位,与登录页默认一致) */
|
||||
const previewName = computed(() => form.company_name.trim() || '衍水集团')
|
||||
/** 保存网页站点信息(只提交站点标题;图标由上传接口即时保存) */
|
||||
async function saveSiteInfo(): Promise<void> {
|
||||
const valid = await siteFormRef.value?.validate().catch(() => false)
|
||||
if (!valid) return
|
||||
savingSite.value = true
|
||||
try {
|
||||
const data = await settingsApi.updateSettings({
|
||||
site_title: siteForm.site_title.trim(),
|
||||
})
|
||||
siteForm.site_title = data.site_title ?? ''
|
||||
logoUrl.value = data.logo_url
|
||||
faviconUrl.value = data.favicon_url
|
||||
settingsStore.apply(data)
|
||||
ElMessage.success('站点信息已保存')
|
||||
} catch (error) {
|
||||
handledError(error)
|
||||
} finally {
|
||||
savingSite.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 图片上传(logo / 站点图标) ====================
|
||||
|
||||
function handleLogoUpload(options: { file: UploadRawFile }): Promise<void> {
|
||||
const file = options.file
|
||||
if (!['image/png', 'image/jpeg'].includes(file.type)) {
|
||||
ElMessage.warning('logo 仅支持 png / jpg 格式')
|
||||
return Promise.resolve()
|
||||
}
|
||||
if (file.size > MAX_IMAGE_BYTES) {
|
||||
ElMessage.warning('logo 文件不能超过 2MB')
|
||||
return Promise.resolve()
|
||||
}
|
||||
uploadingLogo.value = true
|
||||
return settingsApi
|
||||
.uploadLogo(file)
|
||||
.then((data) => {
|
||||
logoUrl.value = data.logo_url
|
||||
faviconUrl.value = data.favicon_url
|
||||
settingsStore.apply(data)
|
||||
ElMessage.success('logo 已更新')
|
||||
})
|
||||
.catch(handledError)
|
||||
.finally(() => {
|
||||
uploadingLogo.value = false
|
||||
})
|
||||
}
|
||||
|
||||
/** 浏览器对 .ico 的 MIME 常为空,改用扩展名判断(后端仍有 magic bytes 兜底) */
|
||||
function handleFaviconUpload(options: { file: UploadRawFile }): Promise<void> {
|
||||
const file = options.file
|
||||
if (!/\.(png|jpe?g|ico)$/i.test(file.name)) {
|
||||
ElMessage.warning('站点图标仅支持 png / jpg / ico 格式')
|
||||
return Promise.resolve()
|
||||
}
|
||||
if (file.size > MAX_IMAGE_BYTES) {
|
||||
ElMessage.warning('站点图标文件不能超过 2MB')
|
||||
return Promise.resolve()
|
||||
}
|
||||
uploadingFavicon.value = true
|
||||
return settingsApi
|
||||
.uploadFavicon(file)
|
||||
.then((data) => {
|
||||
faviconUrl.value = data.favicon_url
|
||||
logoUrl.value = data.logo_url
|
||||
settingsStore.apply(data)
|
||||
ElMessage.success('站点图标已更新')
|
||||
})
|
||||
.catch(handledError)
|
||||
.finally(() => {
|
||||
uploadingFavicon.value = false
|
||||
})
|
||||
}
|
||||
|
||||
// ==================== 预览 ====================
|
||||
|
||||
/** 当前页面名(本页路由标题为「公司信息」),用于标签页标题预览 */
|
||||
const previewPageTitle = computed(() => (route.meta.title as string | undefined) ?? '公司信息')
|
||||
|
||||
const previewName = computed(() => loginForm.company_name.trim() || '衍水集团')
|
||||
const previewSlogan = computed(
|
||||
() => loginForm.brand_slogan.trim() || '把每一笔回款,算成一份踏实。',
|
||||
)
|
||||
const previewFooter = computed(
|
||||
() => loginForm.footer_text.trim() || `© ${previewName.value} · 内部系统`,
|
||||
)
|
||||
const previewIcp = computed(() => loginForm.icp_no.trim())
|
||||
const previewSiteTitle = computed(
|
||||
() => siteForm.site_title.trim() || '营销人员工资管理系统',
|
||||
)
|
||||
const previewTabTitle = computed(
|
||||
() => `${previewPageTitle.value} - ${previewName.value} ${previewSiteTitle.value}`,
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page">
|
||||
<div class="settings-grid">
|
||||
<el-card shadow="never" class="settings-card">
|
||||
<template #header>
|
||||
<span class="card-title">公司信息</span>
|
||||
</template>
|
||||
<!-- 左一:登录页信息 -->
|
||||
<el-form
|
||||
ref="loginFormRef"
|
||||
:model="loginForm"
|
||||
:rules="loginRules"
|
||||
label-width="110px"
|
||||
class="settings-form"
|
||||
@submit.prevent
|
||||
>
|
||||
<el-card shadow="never" class="settings-card">
|
||||
<template #header>
|
||||
<span class="card-title">登录页信息</span>
|
||||
</template>
|
||||
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-width="88px"
|
||||
@submit.prevent
|
||||
>
|
||||
<el-form-item label="公司名称" prop="company_name">
|
||||
<el-input
|
||||
v-model="form.company_name"
|
||||
v-model="loginForm.company_name"
|
||||
maxlength="64"
|
||||
placeholder="展示在登录页与导出页眉"
|
||||
placeholder="展示在登录页、侧边栏与浏览器标题"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="品牌标语" prop="brand_slogan">
|
||||
<el-input
|
||||
v-model="loginForm.brand_slogan"
|
||||
maxlength="64"
|
||||
placeholder="留空使用默认:把每一笔回款,算成一份踏实。"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="页脚版权" prop="footer_text">
|
||||
<el-input
|
||||
v-model="loginForm.footer_text"
|
||||
maxlength="64"
|
||||
placeholder="留空自动生成:© 公司名 · 内部系统"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="ICP 备案号" prop="icp_no">
|
||||
<el-input
|
||||
v-model="loginForm.icp_no"
|
||||
maxlength="32"
|
||||
placeholder="选填,展示在登录页页脚"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="logo">
|
||||
<div class="logo-field">
|
||||
<div class="asset-field">
|
||||
<el-upload
|
||||
class="logo-uploader"
|
||||
:show-file-list="false"
|
||||
:http-request="handleUpload"
|
||||
:before-upload="beforeUpload"
|
||||
:http-request="handleLogoUpload"
|
||||
accept="image/png,image/jpeg"
|
||||
>
|
||||
<div v-loading="uploading" class="logo-box" title="点击上传 png / jpg(≤2MB)">
|
||||
<div v-loading="uploadingLogo" class="asset-box" title="点击上传 png / jpg(≤2MB)">
|
||||
<img v-if="logoUrl" :src="logoUrl" alt="公司 logo" />
|
||||
<span v-else class="logo-plus">+</span>
|
||||
<span v-else class="asset-plus">+</span>
|
||||
</div>
|
||||
</el-upload>
|
||||
<p class="logo-hint">png / jpg,不超过 2MB;<br />展示于登录页品牌位与导出页眉。</p>
|
||||
<p class="asset-hint">
|
||||
png / jpg,不超过 2MB;<br />展示于登录页品牌位。
|
||||
</p>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="submitting" @click="handleSubmit">保存</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
<div class="card-actions">
|
||||
<el-button type="primary" :loading="savingLogin" @click="saveLoginInfo">
|
||||
保存
|
||||
</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-form>
|
||||
|
||||
<!-- 签名元素:登录页实景预览(缩比登录卡,品牌名/logo 即时反映) -->
|
||||
<el-card shadow="never" class="preview-card">
|
||||
<!-- 右一:登录页预览(由左侧「登录页信息」控制) -->
|
||||
<el-card shadow="never" class="settings-card preview-card">
|
||||
<template #header>
|
||||
<span class="card-title">登录页预览</span>
|
||||
<span class="preview-note">保存后对所有人生效</span>
|
||||
<span class="preview-note">对应左侧「登录页信息」</span>
|
||||
</template>
|
||||
<div class="login-mock">
|
||||
<div class="mock-brand">
|
||||
<img v-if="logoUrl" :src="logoUrl" alt="" class="mock-logo" />
|
||||
<div class="mock-name">{{ previewName }}</div>
|
||||
<p class="mock-slogan">把每一笔回款,算成一份踏实。</p>
|
||||
<p class="mock-slogan">{{ previewSlogan }}</p>
|
||||
<span class="mock-waterline"></span>
|
||||
</div>
|
||||
<div class="mock-field"></div>
|
||||
<div class="mock-field"></div>
|
||||
<div class="mock-submit">登 录</div>
|
||||
</div>
|
||||
<p class="mock-foot">
|
||||
{{ previewFooter }}<template v-if="previewIcp"> · {{ previewIcp }}</template>
|
||||
</p>
|
||||
<p class="preview-caption">登录页品牌位实景(示意,非交互)</p>
|
||||
</el-card>
|
||||
|
||||
<!-- 左二:网页站点信息 -->
|
||||
<el-form
|
||||
ref="siteFormRef"
|
||||
:model="siteForm"
|
||||
:rules="siteRules"
|
||||
label-width="110px"
|
||||
class="settings-form"
|
||||
@submit.prevent
|
||||
>
|
||||
<el-card shadow="never" class="settings-card">
|
||||
<template #header>
|
||||
<span class="card-title">网页站点信息</span>
|
||||
</template>
|
||||
|
||||
<el-form-item label="站点标题" prop="site_title">
|
||||
<el-input
|
||||
v-model="siteForm.site_title"
|
||||
maxlength="64"
|
||||
placeholder="留空使用默认:营销人员工资管理系统"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="站点图标">
|
||||
<div class="asset-field">
|
||||
<el-upload
|
||||
:show-file-list="false"
|
||||
:http-request="handleFaviconUpload"
|
||||
accept="image/png,image/jpeg,image/x-icon,image/vnd.microsoft.icon,.ico"
|
||||
>
|
||||
<div
|
||||
v-loading="uploadingFavicon"
|
||||
class="asset-box asset-box--small"
|
||||
title="点击上传 png / jpg / ico(≤2MB)"
|
||||
>
|
||||
<!-- 未自定义时显示公司 logo(跟随 logo 变化) -->
|
||||
<img :src="effectiveFavicon" alt="站点图标" />
|
||||
</div>
|
||||
</el-upload>
|
||||
<p class="asset-hint">
|
||||
png / jpg / ico,不超过 2MB;<br />
|
||||
未上传时跟随公司 logo,展示于浏览器标签页。
|
||||
</p>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<div class="card-actions">
|
||||
<el-button type="primary" :loading="savingSite" @click="saveSiteInfo">保存</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-form>
|
||||
|
||||
<!-- 右二:浏览器标签页预览(由左侧「网页站点信息」控制) -->
|
||||
<el-card shadow="never" class="settings-card preview-card tab-preview-card">
|
||||
<template #header>
|
||||
<span class="card-title">浏览器标签页预览</span>
|
||||
<span class="preview-note">对应左侧「网页站点信息」</span>
|
||||
</template>
|
||||
<div class="tab-bar">
|
||||
<div class="tab-bar__tab">
|
||||
<img :src="effectiveFavicon" alt="" class="tab-bar__icon" />
|
||||
<span class="tab-bar__title">{{ previewTabTitle }}</span>
|
||||
<span class="tab-bar__close">×</span>
|
||||
</div>
|
||||
<span class="tab-bar__plus">+</span>
|
||||
</div>
|
||||
<p class="preview-caption">浏览器标签页实景(标题与图标)</p>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -163,9 +397,41 @@ const previewName = computed(() => form.company_name.trim() || '衍水集团')
|
||||
<style scoped>
|
||||
.settings-grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(360px, 460px) minmax(300px, 380px);
|
||||
grid-template-columns: minmax(420px, 560px) minmax(300px, 380px);
|
||||
gap: 16px;
|
||||
align-items: start;
|
||||
/* 同一行左右卡片等高:右侧预览卡拉伸到与左侧设置卡相同的底边 */
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.settings-form {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.settings-form :deep(.el-card) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 预览卡铺满行高,内容区可纵向伸展 */
|
||||
.preview-card,
|
||||
.tab-preview-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.preview-card :deep(.el-card__body),
|
||||
.tab-preview-card :deep(.el-card__body) {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 登录页实景卡随可用高度放大:两个模拟输入框吃掉多余高度,
|
||||
整张登录卡纵向撑满预览卡,底边与左侧「登录页信息」对齐 */
|
||||
.login-mock {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
@@ -178,13 +444,19 @@ const previewName = computed(() => form.company_name.trim() || '衍水集团')
|
||||
color: var(--ys-ink-3);
|
||||
}
|
||||
|
||||
.logo-field {
|
||||
.card-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.asset-field {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.logo-box {
|
||||
.asset-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -198,24 +470,29 @@ const previewName = computed(() => form.company_name.trim() || '衍水集团')
|
||||
transition: border-color 150ms ease;
|
||||
}
|
||||
|
||||
.logo-box:hover {
|
||||
.asset-box--small {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
.asset-box:hover {
|
||||
border-color: var(--ys-indigo);
|
||||
}
|
||||
|
||||
.logo-box img {
|
||||
.asset-box img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.logo-plus {
|
||||
.asset-plus {
|
||||
font-size: 26px;
|
||||
font-weight: 300;
|
||||
line-height: 1;
|
||||
color: var(--ys-ink-3);
|
||||
}
|
||||
|
||||
.logo-hint {
|
||||
.asset-hint {
|
||||
margin: 4px 0 0;
|
||||
font-size: 12px;
|
||||
line-height: 1.7;
|
||||
@@ -268,7 +545,8 @@ const previewName = computed(() => form.company_name.trim() || '衍水集团')
|
||||
}
|
||||
|
||||
.mock-field {
|
||||
height: 32px;
|
||||
flex: 1;
|
||||
min-height: 32px;
|
||||
margin-top: 12px;
|
||||
background: #f4f5f8;
|
||||
border: 1px solid rgba(28, 35, 51, 0.07);
|
||||
@@ -287,13 +565,74 @@ const previewName = computed(() => form.company_name.trim() || '衍水集团')
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.preview-caption {
|
||||
margin: 10px 0 0;
|
||||
.mock-foot {
|
||||
margin: 12px 0 0;
|
||||
font-size: 11px;
|
||||
text-align: center;
|
||||
color: var(--ys-ink-3);
|
||||
}
|
||||
|
||||
.preview-caption {
|
||||
margin: 8px 0 0;
|
||||
font-size: 11px;
|
||||
text-align: center;
|
||||
color: var(--ys-ink-3);
|
||||
}
|
||||
|
||||
/* ── 浏览器标签页预览(示意;与登录页预览分成两张卡,对应左侧第二张卡) ── */
|
||||
.tab-preview-card :deep(.el-card__body) {
|
||||
/* 行高由左侧「网页站点信息」决定:标签页示意在卡内垂直居中 */
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.tab-bar {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 6px;
|
||||
padding: 10px 12px 0;
|
||||
background: #e9ebf1;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.tab-bar__tab {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
min-width: 0;
|
||||
padding: 9px 12px;
|
||||
background: #ffffff;
|
||||
border-radius: 8px 8px 0 0;
|
||||
box-shadow: 0 -1px 2px rgba(28, 35, 51, 0.06);
|
||||
}
|
||||
|
||||
.tab-bar__icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
flex-shrink: 0;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.tab-bar__title {
|
||||
overflow: hidden;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
color: var(--ys-ink-2);
|
||||
}
|
||||
|
||||
.tab-bar__close {
|
||||
flex-shrink: 0;
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
color: var(--ys-ink-3);
|
||||
}
|
||||
|
||||
.tab-bar__plus {
|
||||
padding: 0 6px 10px;
|
||||
font-size: 14px;
|
||||
color: var(--ys-ink-3);
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.settings-grid {
|
||||
grid-template-columns: 1fr;
|
||||
|
||||
Reference in new issue
Block a user