831 lines
23 KiB
Vue
831 lines
23 KiB
Vue
<template>
|
|
<view class="ent-card" :style="[designVars, { fontFamily: ds.fontFamily }]">
|
|
<view class="page-layer">
|
|
<!-- 顶部英雄区: 蓝色渐变圆角卡(对齐后端 wap 参考图) -->
|
|
<view class="hero" v-if="headerSrc || staffList.length">
|
|
<view class="hero-pill">
|
|
<view class="hero-pill-dot"></view>
|
|
<text>数字名片</text>
|
|
</view>
|
|
|
|
<!-- staff 模式: 员工名片轮播 -->
|
|
<swiper v-if="headerMode === 'staff' && staffList.length" class="hero-swiper"
|
|
:indicator-dots="staffList.length > 1" indicator-active-color="rgba(255,255,255,0.9)"
|
|
indicator-color="rgba(255,255,255,0.4)" :autoplay="staffList.length > 1" circular
|
|
@change="onStaffChange">
|
|
<swiper-item v-for="(s, i) in staffList" :key="i">
|
|
<view class="hero-id">
|
|
<image v-if="s.avatar" class="hi-avatar" :src="$util.img(s.avatar)" mode="aspectFill" />
|
|
<view v-else class="hi-avatar hi-avatar-ph">
|
|
<text class="hi-avatar-char">企</text>
|
|
</view>
|
|
<view class="hi-info">
|
|
<view class="hi-name">{{ s.name }}</view>
|
|
<view class="hi-job" v-if="s.job_title">{{ s.job_title }}</view>
|
|
</view>
|
|
</view>
|
|
</swiper-item>
|
|
</swiper>
|
|
|
|
<!-- official / self: 单人信息 -->
|
|
<view v-else class="hero-id">
|
|
<image v-if="headerSrc && headerSrc.avatar" class="hi-avatar" :src="$util.img(headerSrc.avatar)" mode="aspectFill" />
|
|
<view v-else class="hi-avatar hi-avatar-ph">
|
|
<text class="hi-avatar-char">企</text>
|
|
</view>
|
|
<view class="hi-info">
|
|
<view class="hi-name">{{ headerSrc.name || '企业名片' }}</view>
|
|
<view class="hi-job" v-if="headerSrc.job_title">{{ headerSrc.job_title }}</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 快捷操作条: 压在英雄卡下沿(参考图: 打电话/发邮件/官方网站/存通讯录) -->
|
|
<view class="quick" v-if="headerSrc">
|
|
<view class="q-item" @tap="onQuickCall">
|
|
<view class="q-icon q-icon-blue">
|
|
<text class="iconfont icon-dianhua"></text>
|
|
</view>
|
|
<text class="q-label">打电话</text>
|
|
</view>
|
|
<view class="q-item" @tap="onQuickEmail">
|
|
<view class="q-icon q-icon-cyan">
|
|
<text class="q-char">@</text>
|
|
</view>
|
|
<text class="q-label">发邮件</text>
|
|
</view>
|
|
<view class="q-item" @tap="onQuickWebsite">
|
|
<view class="q-icon q-icon-blue">
|
|
<text class="q-char">网</text>
|
|
</view>
|
|
<text class="q-label">官方网站</text>
|
|
</view>
|
|
<view class="q-item" @tap="onQuickSave">
|
|
<view class="q-icon q-icon-orange">
|
|
<text class="iconfont icon-yonghu"></text>
|
|
</view>
|
|
<text class="q-label">存通讯录</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 企业简介 -->
|
|
<view class="bio" v-if="fields.bio" :style="{ backgroundColor: ds.bgColor, borderRadius: ds.cardRadius + 'px' }">
|
|
<text class="bio-text" :style="{ color: ds.textColor }">{{ fields.bio }}</text>
|
|
<text class="bio-en" v-if="fields.bio_en" :style="{ color: ds.textSoft }">{{ fields.bio_en }}</text>
|
|
</view>
|
|
|
|
<!-- 联系资料(企业字段) -->
|
|
<view class="contact" v-if="contentRows.length" :style="{ backgroundColor: ds.bgColor, borderRadius: ds.cardRadius + 'px' }">
|
|
<view class="ct-title" :style="{ borderLeftColor: ds.themeColor }">联系资料</view>
|
|
<view v-for="(row, idx) in contentRows" :key="idx" class="ct-row">
|
|
<view class="ct-icon" :style="{ backgroundColor: ds.themeSoft }">
|
|
<text v-if="row.icon" class="iconfont" :class="row.icon" :style="{ color: ds.themeColor }"></text>
|
|
<text v-else class="ct-char" :style="{ color: ds.themeColor }">{{ row.label.slice(0, 1) }}</text>
|
|
</view>
|
|
<text class="ct-label" :style="{ color: ds.textSoft }">{{ row.label }}</text>
|
|
<text class="ct-value" :class="{ 'ct-multi': row.multiline }" :style="{ color: ds.textColor }">{{ row.value }}</text>
|
|
<text v-if="row.copyable" class="ct-copy" @tap="copyText(row.value)">复制</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- self 模式未自选提示 -->
|
|
<view class="self-hint" v-if="headerMode === 'self' && headerSelf && headerSelf.chosen === 0" @tap="openChooseSelf">
|
|
<text class="sh-icon iconfont icon-gonggao" :style="{ color: ds.themeColor }"></text>
|
|
<text class="sh-text">你还没有选定展示名片,当前自动取最新一张</text>
|
|
<text class="sh-btn" :style="{ color: ds.themeColor }">选择</text>
|
|
</view>
|
|
|
|
<!-- 加载中 -->
|
|
<view class="ent-loading" v-if="isLoading">加载中…</view>
|
|
|
|
<!-- 不存在 / 无权查看 -->
|
|
<ns-empty v-if="!isLoading && !headerSrc && !staffList.length" :isIndex="false"
|
|
:text="errorMsg || '企业名片不存在或未生效,请在管理端完善资料并设为生效'"></ns-empty>
|
|
</view>
|
|
|
|
<!-- 底部操作条(参考图: 保存到通讯录 + 分享; 收藏/交换名片为企业卡无对应后端能力, 暂不提供) -->
|
|
<view class="bottom-bar" v-if="headerSrc">
|
|
<view class="bb-btn bb-main" @tap="onQuickSave">保存到通讯录</view>
|
|
<!-- #ifdef MP-WEIXIN -->
|
|
<button class="bb-btn bb-share" open-type="share">分享</button>
|
|
<!-- #endif -->
|
|
<!-- #ifdef H5 || APP-PLUS || MP-HARMONY -->
|
|
<view class="bb-btn bb-share" @tap="openShare">分享</view>
|
|
<!-- #endif -->
|
|
</view>
|
|
|
|
<!-- 员工自选展示名片弹层 -->
|
|
<view class="choose-mask" v-if="showChoose" @tap="closeChoose"></view>
|
|
<view class="choose-sheet" :class="{ 'choose-show': showChoose }">
|
|
<view class="cs-bar"></view>
|
|
<view class="cs-title">选择展示名片</view>
|
|
<view class="cs-list">
|
|
<view v-for="(c, idx) in myCards" :key="idx" class="cs-item" @tap="chooseSelf(c)">
|
|
<view class="cs-avatar" :style="{ backgroundColor: ds.themeColor }">{{ (c.name || '?').slice(0, 1) }}</view>
|
|
<view class="cs-info">
|
|
<view class="cs-name">{{ c.name }}</view>
|
|
<view class="cs-sub" v-if="c.job_title || c.company">{{ [c.job_title, c.company].filter(Boolean).join(' · ') }}</view>
|
|
</view>
|
|
<text v-if="headerSelf && headerSelf.card_id === c.card_id" class="cs-chosen iconfont icon-dui" :style="{ color: ds.themeColor }"></text>
|
|
</view>
|
|
<view class="cs-empty" v-if="!myCards.length">你还没有个人名片</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
function mixHex(a, b, t) {
|
|
const toRgb = h => {
|
|
h = (h || '').replace('#', '');
|
|
if (h.length === 3) h = h.split('').map(c => c + c).join('');
|
|
const n = parseInt(h, 16);
|
|
if (isNaN(n)) return [0, 0, 0];
|
|
return [(n >> 16) & 255, (n >> 8) & 255, n & 255];
|
|
};
|
|
const [r1, g1, b1] = toRgb(a);
|
|
const [r2, g2, b2] = toRgb(b);
|
|
const r = Math.round(r1 + (r2 - r1) * t);
|
|
const g = Math.round(g1 + (g2 - g1) * t);
|
|
const bl = Math.round(b1 + (b2 - b1) * t);
|
|
return '#' + [r, g, bl].map(x => x.toString(16).padStart(2, '0')).join('');
|
|
}
|
|
|
|
export default {
|
|
data() {
|
|
return {
|
|
cardId: '',
|
|
isLoading: true,
|
|
errorMsg: '',
|
|
data: null, // 企业名片详情(后端 ENTERPRISE_UNIAPP_GUIDE §三)
|
|
header: null, // {mode,official_card_id,official,self,staff}
|
|
fields: {}, // 企业信息(内容区)
|
|
design: null, // {theme_color,bg_color,text_color,template_id,canvas,header_mode,official_card_id}
|
|
staffIndex: 0, // staff 轮播当前项(存通讯录按当前展示的员工取字段)
|
|
showChoose: false,
|
|
myCards: []
|
|
};
|
|
},
|
|
computed: {
|
|
headerMode() {
|
|
return (this.header && this.header.mode) || 'official';
|
|
},
|
|
headerSrc() {
|
|
const h = this.header || {};
|
|
let src;
|
|
if (this.headerMode === 'official') src = h.official || this.fields;
|
|
else if (this.headerMode === 'self') src = h.self || this.fields;
|
|
else return null; // staff 走轮播
|
|
// 注意: 空对象 {} 在 JS 里是真值, 必须显式判空,
|
|
// 否则后端返回空数据时会渲染出"有壳没内容"的空页面且空态提示也不出现
|
|
return src && Object.keys(src).length ? src : null;
|
|
},
|
|
staffList() {
|
|
return (this.header && this.header.staff) || [];
|
|
},
|
|
staffCurrent() {
|
|
return this.staffList[this.staffIndex] || null;
|
|
},
|
|
headerSelf() {
|
|
return (this.header && this.header.self) || null;
|
|
},
|
|
// 名片区当前展示的那个人(official/self 单人 或 staff 当前轮播项)
|
|
displayPerson() {
|
|
if (this.headerMode === 'staff') return this.staffCurrent || {};
|
|
return this.headerSrc || {};
|
|
},
|
|
contentRows() {
|
|
const f = this.fields || {};
|
|
const rows = [];
|
|
const push = (label, value, opts) => {
|
|
if (value !== undefined && value !== null && String(value).trim() !== '') {
|
|
rows.push(Object.assign({ label, value: String(value) }, opts || {}));
|
|
}
|
|
};
|
|
push('手机', f.mobile, { icon: 'icon-shouji1', copyable: true });
|
|
push('邮箱', f.email, { copyable: true });
|
|
push('网站', f.website, { copyable: true });
|
|
push('地址', f.address, { icon: 'icon-dizhi' });
|
|
push('企业名称', f.name);
|
|
return rows;
|
|
},
|
|
/* ============ design 归一化(与 detail.vue 同源, 仅做有限主题化) ============ */
|
|
ds() {
|
|
const d = this.design || {};
|
|
const FONTS = {
|
|
system: '-apple-system,BlinkMacSystemFont,"PingFang SC","Helvetica Neue",Arial,sans-serif',
|
|
sans: '"Source Han Sans SC","Noto Sans SC","PingFang SC",Helvetica,Arial,sans-serif',
|
|
serif: '"Source Han Serif SC","Songti SC",SimSun,Georgia,serif',
|
|
rounded: '"Yuanti SC","YouYuan","Quicksand",-apple-system,sans-serif'
|
|
};
|
|
const isHex = v => /^#[0-9a-fA-F]{3,8}$/.test(v || '');
|
|
const theme = isHex(d.theme_color) ? d.theme_color : '#119dff';
|
|
const text = isHex(d.text_color) ? d.text_color : '#1f2329';
|
|
const bg = isHex(d.bg_color) ? d.bg_color : '#ffffff';
|
|
const fontKey = FONTS[d.font_family] ? d.font_family : 'system';
|
|
const fontScale = Math.min(160, Math.max(60, Number(d.font_scale) || 100));
|
|
return {
|
|
themeColor: theme,
|
|
textColor: text,
|
|
bgColor: bg,
|
|
fontFamily: FONTS[fontKey],
|
|
cardRadius: Number(d.card_radius) || 16,
|
|
fontScaleNum: fontScale / 100,
|
|
textSoft: mixHex(text, bg, 0.55),
|
|
themeSoft: mixHex(theme, '#ffffff', 0.85),
|
|
themeBorder: mixHex(theme, '#ffffff', 0.55),
|
|
headerGradientEnd: typeof d.header_gradient_end === 'string' ? d.header_gradient_end : ''
|
|
};
|
|
},
|
|
designVars() {
|
|
const s = this.ds;
|
|
return {
|
|
'--card-theme': s.themeColor,
|
|
'--card-theme-end': s.headerGradientEnd || s.themeColor,
|
|
'--card-theme-soft': s.themeSoft,
|
|
'--card-theme-border': s.themeBorder,
|
|
'--card-text': s.textColor,
|
|
'--card-text-soft': s.textSoft,
|
|
'--card-bg': s.bgColor,
|
|
'--card-radius': s.cardRadius + 'px',
|
|
'--card-font': s.fontFamily,
|
|
'--card-font-scale': s.fontScaleNum
|
|
};
|
|
}
|
|
},
|
|
onLoad(options) {
|
|
this.cardId = (options && options.card_id) ? decodeURIComponent(options.card_id) : '';
|
|
this.loadData();
|
|
},
|
|
onReady() {
|
|
uni.setNavigationBarTitle({ title: '企业名片' });
|
|
},
|
|
onShow() {
|
|
// 每次显示都兜底标题(微信返回本页时可能恢复成旧值); 从自选弹层返回也刷新
|
|
uni.setNavigationBarTitle({ title: '企业名片' });
|
|
// #ifdef H5
|
|
document.title = '企业名片';
|
|
// #endif
|
|
if (this._loadedOnce) this.loadData();
|
|
this._loadedOnce = true;
|
|
},
|
|
onPullDownRefresh() {
|
|
this.loadData();
|
|
setTimeout(() => uni.stopPullDownRefresh(), 300);
|
|
},
|
|
onShareAppMessage() {
|
|
const name = (this.fields && this.fields.name) || '企业名片';
|
|
return {
|
|
title: name + ' - 企业名片',
|
|
path: '/pages_tool/card/enterprise' + (this.cardId ? '?card_id=' + encodeURIComponent(this.cardId) : ''),
|
|
imageUrl: this.displayPerson && this.displayPerson.avatar ? this.$util.img(this.displayPerson.avatar) : ''
|
|
};
|
|
},
|
|
methods: {
|
|
loadData(retry) {
|
|
this.isLoading = true;
|
|
this.$cardApi.enterpriseDetail({
|
|
data: this.cardId ? { card_id: this.cardId } : {},
|
|
success: res => {
|
|
// 输出原始响应, 便于真机排查"刷新后数据变空"类问题(控制台看后端到底回了什么)
|
|
console.log('[enterprise] detail resp', this.cardId || '(无card_id)', res);
|
|
if (res.code == 0) {
|
|
const d = res.data || {};
|
|
const emptyCard = !d.header && (!d.fields || !Object.keys(d.fields).length);
|
|
if (emptyCard && (this.header || Object.keys(this.fields).length)) {
|
|
// 已有数据时刷新却拿到空卡(多为后端"当前生效卡"与所查卡不一致):
|
|
// 保留原数据, 避免"刷新一下内容就没了"
|
|
console.warn('[enterprise] 刷新返回空卡, 保留原数据', d);
|
|
} else {
|
|
this.data = d;
|
|
this.header = d.header || null;
|
|
this.fields = d.fields || {};
|
|
this.design = d.design || null;
|
|
}
|
|
this.errorMsg = '';
|
|
this.isLoading = false;
|
|
} else if (res.code == -1 && !retry) {
|
|
// -1 = 尚未登录: 冷启动/刚返回本页时 store 的 token 可能还没恢复完成,
|
|
// 属瞬时态, 静默重试一次再决定是否展示错误(与 detail.vue 同策略)
|
|
setTimeout(() => this.loadData(true), 800);
|
|
} else {
|
|
// 登录类错误(-1/-10009 等)不展示"去登录"死路:
|
|
// 本页是客户视角分享页, 按后端对接文档 §五 游客也应可看(待后端放开),
|
|
// 这里给中性可重试文案; 其余错误原样带出
|
|
const msg = res.message || '';
|
|
const isLoginErr = res.code == -1 || /登录/.test(msg);
|
|
this.errorMsg = isLoginErr ? '名片加载失败,请下拉重试' : (msg || '加载失败');
|
|
this.isLoading = false;
|
|
}
|
|
},
|
|
fail: () => {
|
|
// 失败时保留原数据(不清空), 仅在首次无数据时展示错误
|
|
if (!this.header && !Object.keys(this.fields).length) {
|
|
this.errorMsg = '网络异常,请下拉重试';
|
|
}
|
|
this.isLoading = false;
|
|
}
|
|
});
|
|
},
|
|
onStaffChange(e) {
|
|
this.staffIndex = (e.detail && e.detail.current) || 0;
|
|
},
|
|
copyText(v) {
|
|
uni.setClipboardData({
|
|
data: String(v || ''),
|
|
success: () => uni.showToast({ title: '已复制', icon: 'success' })
|
|
});
|
|
},
|
|
/* ===== 快捷操作(参考图四项) ===== */
|
|
onQuickCall() {
|
|
const m = this.displayPerson.mobile || this.fields.mobile;
|
|
if (!m) return uni.showToast({ title: '未填写手机号', icon: 'none' });
|
|
uni.makePhoneCall({ phoneNumber: String(m) });
|
|
},
|
|
onQuickEmail() {
|
|
const e = this.displayPerson.email || this.fields.email;
|
|
if (!e) return uni.showToast({ title: '未填写邮箱', icon: 'none' });
|
|
this.copyText(e);
|
|
},
|
|
onQuickWebsite() {
|
|
const w = this.displayPerson.website || this.fields.website;
|
|
if (!w) return uni.showToast({ title: '未填写官网', icon: 'none' });
|
|
// 小程序内无法直接打开外部网页(web-view 中转体验差), 复制链接到浏览器打开
|
|
this.copyText(w);
|
|
uni.showToast({ title: '链接已复制,请在浏览器打开', icon: 'none' });
|
|
},
|
|
onQuickSave() {
|
|
// 跳原生页直接调 addPhoneContact(web-view 内 H5 才受限, 原生页可直存)
|
|
const p = this.displayPerson || {};
|
|
const q = [
|
|
'name=' + encodeURIComponent(p.name || this.fields.name || ''),
|
|
'mobile=' + encodeURIComponent(p.mobile || this.fields.mobile || ''),
|
|
'company=' + encodeURIComponent(this.fields.name || ''),
|
|
'job_title=' + encodeURIComponent(p.job_title || ''),
|
|
'email=' + encodeURIComponent(p.email || this.fields.email || ''),
|
|
'website=' + encodeURIComponent(p.website || this.fields.website || ''),
|
|
'address=' + encodeURIComponent(p.address || this.fields.address || ''),
|
|
'wechat=' + encodeURIComponent(p.wechat || '')
|
|
].join('&');
|
|
uni.navigateTo({ url: '/pages_tool/card/save-contact?' + q });
|
|
},
|
|
openChooseSelf() {
|
|
this.$cardApi.cardLists({
|
|
data: { page: 1, page_size: 100 },
|
|
success: res => {
|
|
const d = res.data || {};
|
|
this.myCards = d.list || [];
|
|
this.showChoose = true;
|
|
},
|
|
fail: () => uni.showToast({ title: '加载名片失败', icon: 'none' })
|
|
});
|
|
},
|
|
closeChoose() {
|
|
this.showChoose = false;
|
|
},
|
|
chooseSelf(c) {
|
|
if (!c || !c.card_id) return;
|
|
this.$cardApi.enterpriseChooseSelf({
|
|
data: { card_id: c.card_id },
|
|
success: res => {
|
|
if (res.code == 0) {
|
|
uni.showToast({ title: '已选择展示名片', icon: 'success' });
|
|
this.showChoose = false;
|
|
this.loadData();
|
|
} else {
|
|
uni.showToast({ title: res.message || '选择失败', icon: 'none' });
|
|
}
|
|
},
|
|
fail: () => uni.showToast({ title: '网络异常', icon: 'none' })
|
|
});
|
|
},
|
|
openShare() {
|
|
uni.showToast({ title: '请点击右上角或浏览器菜单分享', icon: 'none' });
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.ent-card {
|
|
min-height: 100vh;
|
|
background-color: #f5f6fa;
|
|
padding-bottom: 140rpx;
|
|
}
|
|
|
|
.page-layer {
|
|
padding: 24rpx;
|
|
}
|
|
|
|
/* ===== 英雄区 ===== */
|
|
.hero {
|
|
position: relative;
|
|
border-radius: 24rpx;
|
|
padding: 32rpx 32rpx 140rpx;
|
|
background:
|
|
radial-gradient(circle at 88% 18%, rgba(255, 255, 255, 0.14) 0, rgba(255, 255, 255, 0.14) 120rpx, transparent 121rpx),
|
|
linear-gradient(150deg, var(--card-theme, #1e6fd9) 0%, var(--card-theme-end, #123f8f) 100%);
|
|
overflow: visible;
|
|
margin-bottom: 90rpx;
|
|
}
|
|
|
|
.hero-pill {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
padding: 6rpx 20rpx;
|
|
border: 1rpx solid rgba(255, 255, 255, 0.5);
|
|
border-radius: 26rpx;
|
|
color: #ffffff;
|
|
font-size: 22rpx;
|
|
|
|
.hero-pill-dot {
|
|
width: 10rpx;
|
|
height: 10rpx;
|
|
border-radius: 50%;
|
|
background: #6cf9c0;
|
|
margin-right: 10rpx;
|
|
}
|
|
}
|
|
|
|
.hero-swiper {
|
|
height: 180rpx;
|
|
margin-top: 24rpx;
|
|
}
|
|
|
|
.hero-id {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-top: 28rpx;
|
|
|
|
.hi-avatar {
|
|
width: 128rpx;
|
|
height: 128rpx;
|
|
border-radius: 50%;
|
|
background: #ffffff;
|
|
border: 6rpx solid rgba(255, 255, 255, 0.85);
|
|
flex-shrink: 0;
|
|
|
|
&.hi-avatar-ph {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
|
|
.hi-avatar-char {
|
|
font-size: 56rpx;
|
|
font-weight: 600;
|
|
color: var(--card-theme, #1e6fd9);
|
|
}
|
|
}
|
|
}
|
|
|
|
.hi-info {
|
|
margin-left: 28rpx;
|
|
min-width: 0;
|
|
|
|
.hi-name {
|
|
font-size: 40rpx;
|
|
font-weight: 600;
|
|
color: #ffffff;
|
|
}
|
|
|
|
.hi-job {
|
|
margin-top: 10rpx;
|
|
font-size: 26rpx;
|
|
color: rgba(255, 255, 255, 0.85);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ===== 快捷操作条: 上移压住英雄卡下沿 ===== */
|
|
.quick {
|
|
position: relative;
|
|
z-index: 2;
|
|
margin: -130rpx 24rpx 24rpx;
|
|
background: #ffffff;
|
|
border-radius: 20rpx;
|
|
box-shadow: 0 8rpx 30rpx rgba(30, 111, 217, 0.10);
|
|
display: flex;
|
|
padding: 28rpx 0;
|
|
|
|
.q-item {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
|
|
.q-icon {
|
|
width: 88rpx;
|
|
height: 88rpx;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
|
|
&.q-icon-blue {
|
|
background: #e3f0ff;
|
|
|
|
.iconfont,
|
|
.q-char {
|
|
color: #3d8bff;
|
|
font-size: 40rpx;
|
|
}
|
|
}
|
|
|
|
&.q-icon-cyan {
|
|
background: #e2f7f4;
|
|
|
|
.q-char {
|
|
color: #2bb99e;
|
|
font-size: 40rpx;
|
|
font-weight: 600;
|
|
}
|
|
}
|
|
|
|
&.q-icon-orange {
|
|
background: #fff3e0;
|
|
|
|
.iconfont {
|
|
color: #f5a623;
|
|
font-size: 40rpx;
|
|
}
|
|
}
|
|
}
|
|
|
|
.q-label {
|
|
margin-top: 14rpx;
|
|
font-size: 24rpx;
|
|
color: #444;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ===== 企业简介 ===== */
|
|
.bio {
|
|
margin: 0 0 24rpx;
|
|
padding: 28rpx 28rpx;
|
|
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
|
|
|
|
.bio-text {
|
|
display: block;
|
|
font-size: 28rpx;
|
|
line-height: 1.6;
|
|
word-break: break-all;
|
|
}
|
|
|
|
.bio-en {
|
|
display: block;
|
|
margin-top: 12rpx;
|
|
font-size: 24rpx;
|
|
line-height: 1.5;
|
|
word-break: break-all;
|
|
}
|
|
}
|
|
|
|
/* ===== 联系资料 ===== */
|
|
.contact {
|
|
padding: 28rpx 28rpx 12rpx;
|
|
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
|
|
margin-bottom: 24rpx;
|
|
|
|
.ct-title {
|
|
font-size: 30rpx;
|
|
font-weight: 600;
|
|
color: #1f2329;
|
|
padding-left: 16rpx;
|
|
border-left-width: 6rpx;
|
|
border-left-style: solid;
|
|
margin-bottom: 20rpx;
|
|
}
|
|
|
|
.ct-row {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 18rpx 0;
|
|
border-bottom: 1rpx solid #f2f3f5;
|
|
|
|
&:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.ct-icon {
|
|
width: 56rpx;
|
|
height: 56rpx;
|
|
border-radius: 12rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-right: 20rpx;
|
|
flex-shrink: 0;
|
|
|
|
.iconfont {
|
|
font-size: 30rpx;
|
|
}
|
|
|
|
.ct-char {
|
|
font-size: 26rpx;
|
|
font-weight: 600;
|
|
}
|
|
}
|
|
|
|
.ct-label {
|
|
width: 88rpx;
|
|
flex-shrink: 0;
|
|
font-size: 26rpx;
|
|
}
|
|
|
|
.ct-value {
|
|
flex: 1;
|
|
min-width: 0;
|
|
font-size: 28rpx;
|
|
word-break: break-all;
|
|
|
|
&.ct-multi {
|
|
white-space: pre-wrap;
|
|
}
|
|
}
|
|
|
|
.ct-copy {
|
|
margin-left: 16rpx;
|
|
flex-shrink: 0;
|
|
padding: 6rpx 20rpx;
|
|
border-radius: 22rpx;
|
|
background: #f2f3f5;
|
|
font-size: 22rpx;
|
|
color: #666;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ===== self 未自选提示 ===== */
|
|
.self-hint {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 20rpx 24rpx;
|
|
background-color: var(--card-theme-soft, #e8f1fb);
|
|
border: 1rpx solid var(--card-theme-border, #cfe3fb);
|
|
border-radius: 16rpx;
|
|
margin-bottom: 24rpx;
|
|
|
|
.sh-icon {
|
|
margin-right: 12rpx;
|
|
font-size: 30rpx;
|
|
}
|
|
|
|
.sh-text {
|
|
flex: 1;
|
|
min-width: 0;
|
|
font-size: 24rpx;
|
|
color: #666;
|
|
}
|
|
|
|
.sh-btn {
|
|
font-size: 26rpx;
|
|
margin-left: 12rpx;
|
|
}
|
|
}
|
|
|
|
.ent-loading {
|
|
text-align: center;
|
|
padding: 40rpx 0;
|
|
color: #999;
|
|
font-size: 26rpx;
|
|
}
|
|
|
|
/* ===== 底部操作条 ===== */
|
|
.bottom-bar {
|
|
position: fixed;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
display: flex;
|
|
gap: 20rpx;
|
|
padding: 20rpx 24rpx calc(20rpx + env(safe-area-inset-bottom));
|
|
background: #ffffff;
|
|
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.06);
|
|
z-index: 10;
|
|
|
|
.bb-btn {
|
|
flex: 1;
|
|
height: 88rpx;
|
|
line-height: 88rpx;
|
|
text-align: center;
|
|
border-radius: 44rpx;
|
|
font-size: 28rpx;
|
|
|
|
&.bb-main {
|
|
background: linear-gradient(150deg, #3d8bff, #1e6fd9);
|
|
color: #ffffff;
|
|
font-weight: 600;
|
|
}
|
|
|
|
&.bb-share {
|
|
background: #f2f3f5;
|
|
color: #333;
|
|
border: none;
|
|
padding: 0;
|
|
margin: 0;
|
|
|
|
&::after {
|
|
border: none;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ===== 员工自选弹层 ===== */
|
|
.choose-mask {
|
|
position: fixed;
|
|
left: 0;
|
|
right: 0;
|
|
top: 0;
|
|
bottom: 0;
|
|
background: rgba(0, 0, 0, 0.4);
|
|
z-index: 99;
|
|
}
|
|
|
|
.choose-sheet {
|
|
position: fixed;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background: #fff;
|
|
border-radius: 28rpx 28rpx 0 0;
|
|
z-index: 100;
|
|
transform: translateY(100%);
|
|
transition: transform 0.25s ease;
|
|
max-height: 70vh;
|
|
display: flex;
|
|
flex-direction: column;
|
|
|
|
&.choose-show {
|
|
transform: translateY(0);
|
|
}
|
|
|
|
.cs-bar {
|
|
width: 64rpx;
|
|
height: 8rpx;
|
|
background: #ddd;
|
|
border-radius: 4rpx;
|
|
margin: 16rpx auto 8rpx;
|
|
}
|
|
|
|
.cs-title {
|
|
text-align: center;
|
|
font-size: 30rpx;
|
|
font-weight: 600;
|
|
padding: 12rpx 0 20rpx;
|
|
}
|
|
|
|
.cs-list {
|
|
overflow-y: auto;
|
|
padding: 0 24rpx 32rpx;
|
|
}
|
|
|
|
.cs-item {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 20rpx 0;
|
|
border-bottom: 1rpx solid #f2f2f2;
|
|
}
|
|
|
|
.cs-avatar {
|
|
width: 72rpx;
|
|
height: 72rpx;
|
|
border-radius: 50%;
|
|
color: #fff;
|
|
font-size: 32rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-right: 20rpx;
|
|
}
|
|
|
|
.cs-info {
|
|
flex: 1;
|
|
min-width: 0;
|
|
|
|
.cs-name {
|
|
font-size: 30rpx;
|
|
color: #333;
|
|
}
|
|
|
|
.cs-sub {
|
|
font-size: 24rpx;
|
|
color: #999;
|
|
margin-top: 4rpx;
|
|
}
|
|
}
|
|
|
|
.cs-chosen {
|
|
font-size: 36rpx;
|
|
}
|
|
|
|
.cs-empty {
|
|
text-align: center;
|
|
color: #999;
|
|
font-size: 26rpx;
|
|
padding: 40rpx 0;
|
|
}
|
|
}
|
|
</style>
|