|
|
@@ -1,916 +1,19 @@
|
|
|
<!--
|
|
|
- 分类广告设置页
|
|
|
- 用途:配置最多3张轮播图,数据存 xhCategorySetting.advertisement JSON
|
|
|
- 格式:[{"type":1,"img":"path","value":"id1,id2"}] type:1图文 2商品 3分类
|
|
|
+ 分类广告旧独立页:入口已并入 categoryV2 第三 Tab
|
|
|
+ 用途:兼容旧链接,进入后重定向到分类管理广告 Tab,避免两套设置页
|
|
|
-->
|
|
|
<template>
|
|
|
- <view class="page-container">
|
|
|
- <view class="page-header">
|
|
|
- <text class="page-title">分类广告图片</text>
|
|
|
- <text class="page-sub">最多支持上传3张轮播图</text>
|
|
|
- </view>
|
|
|
-
|
|
|
- <view class="ad-card" v-for="(item, index) in adList" :key="index">
|
|
|
- <view class="card-title">轮播图{{ index + 1 }}</view>
|
|
|
-
|
|
|
- <view class="upload-area" @click="chooseImage(index)">
|
|
|
- <block v-if="item.img">
|
|
|
- <image class="banner-img" :src="imgFullUrl(item.img)" mode="aspectFill" />
|
|
|
- <view class="remove-btn" @click.stop="removeImage(index)">×</view>
|
|
|
- </block>
|
|
|
- <block v-else>
|
|
|
- <view class="upload-placeholder">
|
|
|
- <text class="upload-icon">☁</text>
|
|
|
- <text class="upload-text">点击上传图片</text>
|
|
|
- <text class="upload-tip">建议尺寸:750*300,支持 jpg/png</text>
|
|
|
- </view>
|
|
|
- </block>
|
|
|
- </view>
|
|
|
-
|
|
|
- <view class="field-row">
|
|
|
- <text class="field-label">关联类型</text>
|
|
|
- <view class="type-tabs">
|
|
|
- <view
|
|
|
- class="type-tab"
|
|
|
- v-for="opt in typeOptions"
|
|
|
- :key="opt.value"
|
|
|
- :class="{ active: item.type === opt.value }"
|
|
|
- @click="changeType(index, opt.value)"
|
|
|
- >{{ opt.label }}</view>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
-
|
|
|
- <view class="field-row field-row-link" @click="openLinkSelect(index)">
|
|
|
- <text class="field-label">关联内容</text>
|
|
|
- <view class="link-select">
|
|
|
- <text class="link-text" :class="{ placeholder: !getActiveLink(item).valueLabel }">{{ getActiveLink(item).valueLabel || '选择关联内容' }}</text>
|
|
|
- <text class="link-arrow">></text>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
-
|
|
|
- <view class="bottom-space"></view>
|
|
|
-
|
|
|
- <!-- 分类多选弹层:支持搜索,分类较多时便于查找 -->
|
|
|
- <view v-if="categoryPickerShow" class="picker-overlay" @touchmove.stop.prevent="preventTouchMove">
|
|
|
- <view class="picker-mask" @tap.stop="closeCategoryPicker"></view>
|
|
|
- <view class="picker-panel category-picker-panel">
|
|
|
- <view class="picker-head">
|
|
|
- <text class="picker-title">选择分类</text>
|
|
|
- <text class="picker-count">已选 {{ categoryPickCount }} 个</text>
|
|
|
- <text class="picker-close" @tap.stop="closeCategoryPicker">×</text>
|
|
|
- </view>
|
|
|
- <view class="picker-search-row">
|
|
|
- <view class="picker-search-box">
|
|
|
- <text class="picker-search-icon">⌕</text>
|
|
|
- <input
|
|
|
- v-model="categorySearchText"
|
|
|
- class="picker-search-input"
|
|
|
- placeholder="搜索分类名称"
|
|
|
- confirm-type="search"
|
|
|
- />
|
|
|
- </view>
|
|
|
- <text
|
|
|
- v-if="categorySearchText"
|
|
|
- class="picker-search-clear"
|
|
|
- @tap.stop="clearCategorySearch"
|
|
|
- >清空</text>
|
|
|
- </view>
|
|
|
- <scroll-view scroll-y class="picker-body">
|
|
|
- <block v-if="filteredBouquetCategories.length">
|
|
|
- <view
|
|
|
- class="picker-item"
|
|
|
- v-for="cat in filteredBouquetCategories"
|
|
|
- :key="cat.id"
|
|
|
- :data-id="cat.id"
|
|
|
- @tap.stop="toggleCategoryPickByEvent"
|
|
|
- >
|
|
|
- <view class="check-box" :class="{ checked: !!categoryPickMap[String(cat.id)] }">
|
|
|
- <text v-if="categoryPickMap[String(cat.id)]" class="check-mark">✓</text>
|
|
|
- </view>
|
|
|
- <text class="picker-item-name">{{ cat.categoryName }}</text>
|
|
|
- </view>
|
|
|
- </block>
|
|
|
- <view v-else class="picker-empty">
|
|
|
- <text>{{ categorySearchText ? '未找到匹配的分类' : '暂无分类' }}</text>
|
|
|
- </view>
|
|
|
- </scroll-view>
|
|
|
- <view class="picker-footer">
|
|
|
- <button class="picker-btn cancel" hover-class="none" @click="closeCategoryPicker">取消</button>
|
|
|
- <button class="picker-btn confirm" hover-class="none" @click="confirmCategoryPick">确定</button>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
-
|
|
|
- <!-- 预览 -->
|
|
|
- <view v-if="previewShow" class="picker-overlay" @touchmove.stop.prevent="preventTouchMove">
|
|
|
- <view class="picker-mask" @click.stop="previewShow = false"></view>
|
|
|
- <view class="preview-panel">
|
|
|
- <swiper class="preview-swiper" indicator-dots circular>
|
|
|
- <swiper-item v-for="(slide, idx) in previewSlides" :key="idx">
|
|
|
- <image class="preview-img" :src="slide" mode="aspectFit" />
|
|
|
- </swiper-item>
|
|
|
- </swiper>
|
|
|
- <button class="preview-close" hover-class="none" @click="previewShow = false">关闭预览</button>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
-
|
|
|
- <view class="bottom-bar">
|
|
|
- <button class="bar-btn bar-btn-preview" hover-class="none" @click="previewAd">
|
|
|
- <text class="preview-eye">👁</text>
|
|
|
- <text>预览</text>
|
|
|
- </button>
|
|
|
- <button class="bar-btn bar-btn-save" hover-class="none" @click="saveAd">保存</button>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
+ <view></view>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
-import { APIHOST } from '@/config';
|
|
|
-import { categoryListInfo } from '@/api/goods';
|
|
|
-import { categorySettingDetail, categorySettingSaveAdvertisement } from '@/api/category-setting';
|
|
|
-
|
|
|
-const EMPTY_LINK = () => ({ value: '', valueLabel: '' });
|
|
|
-
|
|
|
-const EMPTY_SLOT = () => ({
|
|
|
- type: 1,
|
|
|
- img: '',
|
|
|
- // 各关联类型独立缓存,切换类型时不丢失已选内容
|
|
|
- links: {
|
|
|
- 1: EMPTY_LINK(),
|
|
|
- 2: EMPTY_LINK(),
|
|
|
- 3: EMPTY_LINK()
|
|
|
- }
|
|
|
-});
|
|
|
-
|
|
|
export default {
|
|
|
name: 'category-ad',
|
|
|
- data() {
|
|
|
- return {
|
|
|
- constant: this.$constant,
|
|
|
- settingId: 0,
|
|
|
- adList: [EMPTY_SLOT(), EMPTY_SLOT(), EMPTY_SLOT()],
|
|
|
- typeOptions: [
|
|
|
- { label: '图文', value: 1 },
|
|
|
- { label: '商品', value: 2 },
|
|
|
- { label: '分类', value: 3 }
|
|
|
- ],
|
|
|
- bouquetCategories: [],
|
|
|
- categoryPickerShow: false,
|
|
|
- categoryPickIndex: 0,
|
|
|
- categoryPickMap: {},
|
|
|
- categorySearchText: '',
|
|
|
- previewShow: false,
|
|
|
- saving: false
|
|
|
- };
|
|
|
- },
|
|
|
- computed: {
|
|
|
- previewSlides() {
|
|
|
- return this.adList
|
|
|
- .filter((item) => item.img)
|
|
|
- .map((item) => this.imgFullUrl(item.img));
|
|
|
- },
|
|
|
- /** 分类弹层内已选数量 */
|
|
|
- categoryPickCount() {
|
|
|
- return Object.keys(this.categoryPickMap).length;
|
|
|
- },
|
|
|
- /** 按关键词过滤花束分类,分类多时可快速定位 */
|
|
|
- filteredBouquetCategories() {
|
|
|
- const keyword = (this.categorySearchText || '').trim().toLowerCase();
|
|
|
- if (!keyword) return this.bouquetCategories;
|
|
|
- return this.bouquetCategories.filter((cat) => {
|
|
|
- const name = (cat.categoryName || '').toLowerCase();
|
|
|
- return name.indexOf(keyword) > -1;
|
|
|
- });
|
|
|
- }
|
|
|
- },
|
|
|
onLoad() {
|
|
|
- this.loadSetting();
|
|
|
- this.loadBouquetCategories();
|
|
|
- uni.$on('categoryAdPicTextSelected', this.onPicTextSelected);
|
|
|
- uni.$on('categoryAdGoodsSelected', this.onGoodsSelected);
|
|
|
- },
|
|
|
- onUnload() {
|
|
|
- uni.$off('categoryAdPicTextSelected', this.onPicTextSelected);
|
|
|
- uni.$off('categoryAdGoodsSelected', this.onGoodsSelected);
|
|
|
- },
|
|
|
- methods: {
|
|
|
- preventTouchMove() {},
|
|
|
- imgFullUrl(path) {
|
|
|
- if (!path) return '';
|
|
|
- if (path.indexOf('http') === 0) return path;
|
|
|
- const base = (this.constant.imgUrl || '').replace(/\/$/, '');
|
|
|
- return `${base}/${path.replace(/^\//, '')}`;
|
|
|
- },
|
|
|
- loadSetting() {
|
|
|
- categorySettingDetail().then((res) => {
|
|
|
- if (res.code != 1) return;
|
|
|
- const data = res.data || {};
|
|
|
- this.settingId = data.id || 0;
|
|
|
- this.adList = this.parseAdvertisement(data.advertisement);
|
|
|
- });
|
|
|
- },
|
|
|
- loadBouquetCategories() {
|
|
|
- categoryListInfo().then((res) => {
|
|
|
- const list = res.data || [];
|
|
|
- this.bouquetCategories = list;
|
|
|
- // 分类名称依赖列表,加载后刷新各 slot 分类类型展示文案
|
|
|
- this.adList = this.adList.map((item) => this.refreshSlotLinkLabels(item));
|
|
|
- });
|
|
|
- },
|
|
|
- /** 取当前关联类型下的内容与展示文案 */
|
|
|
- getActiveLink(item) {
|
|
|
- const type = item.type || 1;
|
|
|
- return (item.links && item.links[type]) || EMPTY_LINK();
|
|
|
- },
|
|
|
- /** 写入指定类型的关联内容 */
|
|
|
- setSlotLink(item, type, value, valueLabel) {
|
|
|
- if (!item.links) {
|
|
|
- item.links = { 1: EMPTY_LINK(), 2: EMPTY_LINK(), 3: EMPTY_LINK() };
|
|
|
- }
|
|
|
- this.$set(item.links, type, { value: value || '', valueLabel: valueLabel || '' });
|
|
|
- },
|
|
|
- /** 分类列表就绪后,重算 slot 内各类型展示文案 */
|
|
|
- refreshSlotLinkLabels(item) {
|
|
|
- const links = item.links || { 1: EMPTY_LINK(), 2: EMPTY_LINK(), 3: EMPTY_LINK() };
|
|
|
- [1, 2, 3].forEach((type) => {
|
|
|
- const link = links[type] || EMPTY_LINK();
|
|
|
- if (link.value) {
|
|
|
- links[type] = {
|
|
|
- value: link.value,
|
|
|
- valueLabel: this.buildValueLabel(type, link.value, link.valueLabel)
|
|
|
- };
|
|
|
- }
|
|
|
- });
|
|
|
- return { ...item, links };
|
|
|
- },
|
|
|
- parseAdvertisement(raw) {
|
|
|
- const slots = [EMPTY_SLOT(), EMPTY_SLOT(), EMPTY_SLOT()];
|
|
|
- if (!raw) return slots;
|
|
|
- let arr = [];
|
|
|
- try {
|
|
|
- arr = typeof raw === 'string' ? JSON.parse(raw) : raw;
|
|
|
- } catch (e) {
|
|
|
- return slots;
|
|
|
- }
|
|
|
- if (!Array.isArray(arr)) return slots;
|
|
|
- arr.slice(0, 3).forEach((item, index) => {
|
|
|
- const type = parseInt(item.type, 10) || 1;
|
|
|
- const value = item.value ? String(item.value) : '';
|
|
|
- const links = {
|
|
|
- 1: EMPTY_LINK(),
|
|
|
- 2: EMPTY_LINK(),
|
|
|
- 3: EMPTY_LINK()
|
|
|
- };
|
|
|
- links[type] = {
|
|
|
- value,
|
|
|
- valueLabel: this.buildValueLabel(type, value)
|
|
|
- };
|
|
|
- slots[index] = {
|
|
|
- type,
|
|
|
- img: item.img || '',
|
|
|
- links
|
|
|
- };
|
|
|
- });
|
|
|
- return slots;
|
|
|
- },
|
|
|
- buildValueLabel(type, value, fallbackLabel) {
|
|
|
- if (!value) return '';
|
|
|
- const ids = value.split(',').filter(Boolean);
|
|
|
- if (type === 1) {
|
|
|
- if (fallbackLabel && fallbackLabel.indexOf('图文ID:') !== 0) return fallbackLabel;
|
|
|
- return ids.length ? `图文ID:${ids.join(',')}` : '';
|
|
|
- }
|
|
|
- if (type === 2) return `已选${ids.length}个商品`;
|
|
|
- if (type === 3) {
|
|
|
- const names = ids.map((id) => {
|
|
|
- const cat = this.bouquetCategories.find((c) => String(c.id) === String(id));
|
|
|
- return cat ? cat.categoryName : id;
|
|
|
- });
|
|
|
- return names.join('、') || `已选${ids.length}个分类`;
|
|
|
- }
|
|
|
- return '';
|
|
|
- },
|
|
|
- /** 仅切换展示类型,不清空各类型已缓存的关联内容 */
|
|
|
- changeType(index, type) {
|
|
|
- const item = this.adList[index];
|
|
|
- if (item.type === type) return;
|
|
|
- item.type = type;
|
|
|
- },
|
|
|
- openLinkSelect(index) {
|
|
|
- const item = this.adList[index];
|
|
|
- const link = this.getActiveLink(item);
|
|
|
- if (item.type === 1) {
|
|
|
- uni.navigateTo({
|
|
|
- url: `/admin/picText/picTextList?mode=selectAd&slotIndex=${index}&picTextId=${link.value || 0}`
|
|
|
- });
|
|
|
- return;
|
|
|
- }
|
|
|
- if (item.type === 2) {
|
|
|
- uni.navigateTo({
|
|
|
- url: `/admin/goods/ad-goods-select?slotIndex=${index}&selectedIds=${link.value || ''}`
|
|
|
- });
|
|
|
- return;
|
|
|
- }
|
|
|
- if (item.type === 3) {
|
|
|
- this.categoryPickIndex = index;
|
|
|
- this.categorySearchText = '';
|
|
|
- this.categoryPickMap = {};
|
|
|
- const ids = link.value ? link.value.split(',').filter(Boolean) : [];
|
|
|
- ids.forEach((id) => {
|
|
|
- this.$set(this.categoryPickMap, String(id), true);
|
|
|
- });
|
|
|
- this.categoryPickerShow = true;
|
|
|
- }
|
|
|
- },
|
|
|
- onPicTextSelected(payload) {
|
|
|
- const index = payload.slotIndex;
|
|
|
- if (index === undefined || !this.adList[index]) return;
|
|
|
- const valueLabel = payload.title || `图文ID:${payload.picTextId}`;
|
|
|
- this.setSlotLink(this.adList[index], 1, String(payload.picTextId || ''), valueLabel);
|
|
|
- },
|
|
|
- onGoodsSelected(payload) {
|
|
|
- const index = payload.slotIndex;
|
|
|
- if (index === undefined || !this.adList[index]) return;
|
|
|
- const ids = (payload.goodsIds || []).map(String);
|
|
|
- const value = ids.join(',');
|
|
|
- this.setSlotLink(
|
|
|
- this.adList[index],
|
|
|
- 2,
|
|
|
- value,
|
|
|
- ids.length ? `已选${ids.length}个商品` : ''
|
|
|
- );
|
|
|
- },
|
|
|
- /** 从 data-id 切换分类勾选,兼容小程序 tap 传参 */
|
|
|
- toggleCategoryPickByEvent(e) {
|
|
|
- const id = e.currentTarget.dataset.id;
|
|
|
- if (id === undefined || id === null || id === '') return;
|
|
|
- const key = String(id);
|
|
|
- if (this.categoryPickMap[key]) {
|
|
|
- this.$delete(this.categoryPickMap, key);
|
|
|
- } else {
|
|
|
- this.$set(this.categoryPickMap, key, true);
|
|
|
- }
|
|
|
- },
|
|
|
- clearCategorySearch() {
|
|
|
- this.categorySearchText = '';
|
|
|
- },
|
|
|
- closeCategoryPicker() {
|
|
|
- this.categoryPickerShow = false;
|
|
|
- this.categorySearchText = '';
|
|
|
- },
|
|
|
- confirmCategoryPick() {
|
|
|
- const index = this.categoryPickIndex;
|
|
|
- const ids = Object.keys(this.categoryPickMap);
|
|
|
- const value = ids.join(',');
|
|
|
- this.setSlotLink(this.adList[index], 3, value, this.buildValueLabel(3, value));
|
|
|
- this.categoryPickerShow = false;
|
|
|
- this.categorySearchText = '';
|
|
|
- },
|
|
|
- chooseImage(index) {
|
|
|
- uni.chooseImage({
|
|
|
- count: 1,
|
|
|
- sizeType: ['compressed'],
|
|
|
- sourceType: ['album', 'camera'],
|
|
|
- success: (res) => {
|
|
|
- const filePath = res.tempFilePaths[0];
|
|
|
- this.uploadImageFile(filePath, index);
|
|
|
- }
|
|
|
- });
|
|
|
- },
|
|
|
- /** 通过 multipart 上传广告图,存相对路径 shortUrl 供保存与展示 */
|
|
|
- uploadImageFile(filePath, index) {
|
|
|
- uni.showLoading({ title: '上传中', mask: true });
|
|
|
- const token = uni.getStorageSync('token') || '';
|
|
|
- uni.uploadFile({
|
|
|
- url: `${APIHOST}/upload/save-file`,
|
|
|
- filePath,
|
|
|
- name: 'file',
|
|
|
- fileType: 'image',
|
|
|
- header: { token },
|
|
|
- success: (uploadRes) => {
|
|
|
- uni.hideLoading();
|
|
|
- try {
|
|
|
- const res = JSON.parse(uploadRes.data || '{}');
|
|
|
- if (res.code == 1 && res.data) {
|
|
|
- // 保存相对路径,展示时由 imgFullUrl 拼接域名
|
|
|
- this.adList[index].img = res.data.shortUrl || res.data.url || '';
|
|
|
- if (!this.adList[index].img) {
|
|
|
- this.$msg('上传失败');
|
|
|
- }
|
|
|
- return;
|
|
|
- }
|
|
|
- this.$msg(res.message || '上传失败');
|
|
|
- } catch (e) {
|
|
|
- this.$msg('上传失败');
|
|
|
- }
|
|
|
- },
|
|
|
- fail: () => {
|
|
|
- uni.hideLoading();
|
|
|
- this.$msg('上传失败');
|
|
|
- }
|
|
|
- });
|
|
|
- },
|
|
|
- removeImage(index) {
|
|
|
- this.adList[index].img = '';
|
|
|
- },
|
|
|
- previewAd() {
|
|
|
- if (!this.previewSlides.length) {
|
|
|
- this.$msg('请先上传广告图');
|
|
|
- return;
|
|
|
- }
|
|
|
- this.previewShow = true;
|
|
|
- },
|
|
|
- saveAd() {
|
|
|
- const payload = [];
|
|
|
- for (let i = 0; i < this.adList.length; i++) {
|
|
|
- const item = this.adList[i];
|
|
|
- const link = this.getActiveLink(item);
|
|
|
- if (!item.img && !link.value) continue;
|
|
|
- if (item.img && !link.value) {
|
|
|
- this.$msg(`请完善轮播图${i + 1}的关联内容`);
|
|
|
- return;
|
|
|
- }
|
|
|
- if (link.value && !item.img) {
|
|
|
- this.$msg(`请上传轮播图${i + 1}的图片`);
|
|
|
- return;
|
|
|
- }
|
|
|
- payload.push({
|
|
|
- type: item.type,
|
|
|
- img: item.img,
|
|
|
- value: link.value
|
|
|
- });
|
|
|
- }
|
|
|
- if (this.saving) return;
|
|
|
- this.saving = true;
|
|
|
- categorySettingSaveAdvertisement({
|
|
|
- advertisement: JSON.stringify(payload)
|
|
|
- }).then((res) => {
|
|
|
- if (res.code == 1) {
|
|
|
- this.$msg('保存成功');
|
|
|
- setTimeout(() => uni.navigateBack(), 500);
|
|
|
- }
|
|
|
- }).finally(() => {
|
|
|
- this.saving = false;
|
|
|
- });
|
|
|
- }
|
|
|
+ // 旧工作台/应用页入口仍可能落到本页,统一进分类管理广告 Tab
|
|
|
+ uni.redirectTo({
|
|
|
+ url: '/admin/goods/categoryV2?tab=2'
|
|
|
+ });
|
|
|
}
|
|
|
};
|
|
|
</script>
|
|
|
-
|
|
|
-<style lang="scss" scoped>
|
|
|
-.page-container {
|
|
|
- min-height: 100vh;
|
|
|
- background: #f5f5f5;
|
|
|
- padding-bottom: 160rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.page-header {
|
|
|
- padding: 24rpx 30rpx 8rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.page-title {
|
|
|
- display: block;
|
|
|
- font-size: 30rpx;
|
|
|
- color: #333;
|
|
|
- font-weight: 600;
|
|
|
- margin-bottom: 8rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.page-sub {
|
|
|
- font-size: 24rpx;
|
|
|
- color: #999;
|
|
|
-}
|
|
|
-
|
|
|
-.ad-card {
|
|
|
- margin: 20rpx 24rpx 0;
|
|
|
- padding: 24rpx;
|
|
|
- background: #fff;
|
|
|
- border-radius: 12rpx;
|
|
|
- border: 1rpx solid #eee;
|
|
|
-}
|
|
|
-
|
|
|
-.card-title {
|
|
|
- font-size: 28rpx;
|
|
|
- color: #333;
|
|
|
- font-weight: 600;
|
|
|
- margin-bottom: 20rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.upload-area {
|
|
|
- position: relative;
|
|
|
- width: 100%;
|
|
|
- height: 240rpx;
|
|
|
- border-radius: 8rpx;
|
|
|
- overflow: hidden;
|
|
|
- margin-bottom: 24rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.banner-img {
|
|
|
- width: 100%;
|
|
|
- height: 100%;
|
|
|
-}
|
|
|
-
|
|
|
-.remove-btn {
|
|
|
- position: absolute;
|
|
|
- top: 12rpx;
|
|
|
- right: 12rpx;
|
|
|
- width: 44rpx;
|
|
|
- height: 44rpx;
|
|
|
- line-height: 40rpx;
|
|
|
- text-align: center;
|
|
|
- background: rgba(0, 0, 0, 0.45);
|
|
|
- color: #fff;
|
|
|
- border-radius: 50%;
|
|
|
- font-size: 32rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.upload-placeholder {
|
|
|
- width: 100%;
|
|
|
- height: 100%;
|
|
|
- border: 2rpx dashed #52c41a;
|
|
|
- border-radius: 8rpx;
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- background: #fafafa;
|
|
|
-}
|
|
|
-
|
|
|
-.upload-icon {
|
|
|
- font-size: 48rpx;
|
|
|
- color: #52c41a;
|
|
|
- margin-bottom: 12rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.upload-text {
|
|
|
- font-size: 28rpx;
|
|
|
- color: #52c41a;
|
|
|
- margin-bottom: 8rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.upload-tip {
|
|
|
- font-size: 22rpx;
|
|
|
- color: #999;
|
|
|
-}
|
|
|
-
|
|
|
-.field-row {
|
|
|
- margin-bottom: 20rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.field-row-link {
|
|
|
- margin-bottom: 0;
|
|
|
-}
|
|
|
-
|
|
|
-.field-label {
|
|
|
- display: block;
|
|
|
- font-size: 26rpx;
|
|
|
- color: #333;
|
|
|
- margin-bottom: 12rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.type-tabs {
|
|
|
- display: flex;
|
|
|
- flex-direction: row;
|
|
|
-}
|
|
|
-
|
|
|
-.type-tab {
|
|
|
- flex: 1;
|
|
|
- height: 64rpx;
|
|
|
- line-height: 64rpx;
|
|
|
- text-align: center;
|
|
|
- font-size: 26rpx;
|
|
|
- color: #666;
|
|
|
- border: 2rpx solid #e5e5e5;
|
|
|
- background: #fff;
|
|
|
- margin-right: 16rpx;
|
|
|
- border-radius: 8rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.type-tab:last-child {
|
|
|
- margin-right: 0;
|
|
|
-}
|
|
|
-
|
|
|
-.type-tab.active {
|
|
|
- color: #52c41a;
|
|
|
- border-color: #52c41a;
|
|
|
- background: #f6ffed;
|
|
|
-}
|
|
|
-
|
|
|
-.link-select {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- height: 72rpx;
|
|
|
- padding: 0 20rpx;
|
|
|
- border: 1rpx solid #e5e5e5;
|
|
|
- border-radius: 8rpx;
|
|
|
- background: #fff;
|
|
|
-}
|
|
|
-
|
|
|
-.link-text {
|
|
|
- flex: 1;
|
|
|
- font-size: 26rpx;
|
|
|
- color: #333;
|
|
|
- overflow: hidden;
|
|
|
- text-overflow: ellipsis;
|
|
|
- white-space: nowrap;
|
|
|
-}
|
|
|
-
|
|
|
-.link-text.placeholder {
|
|
|
- color: #bbb;
|
|
|
-}
|
|
|
-
|
|
|
-.link-arrow {
|
|
|
- color: #ccc;
|
|
|
- font-size: 28rpx;
|
|
|
- margin-left: 12rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.bottom-space {
|
|
|
- height: 40rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.bottom-bar {
|
|
|
- position: fixed;
|
|
|
- left: 0;
|
|
|
- right: 0;
|
|
|
- bottom: 0;
|
|
|
- display: flex;
|
|
|
- flex-direction: row;
|
|
|
- align-items: center;
|
|
|
- padding: 20rpx 24rpx;
|
|
|
- padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
|
|
- background: #fff;
|
|
|
- box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.06);
|
|
|
- z-index: 100;
|
|
|
-}
|
|
|
-
|
|
|
-.bar-btn {
|
|
|
- height: 80rpx;
|
|
|
- line-height: 80rpx;
|
|
|
- margin: 0;
|
|
|
- padding: 0;
|
|
|
- font-size: 28rpx;
|
|
|
- border-radius: 8rpx;
|
|
|
- text-align: center;
|
|
|
-}
|
|
|
-
|
|
|
-.bar-btn::after {
|
|
|
- border: none;
|
|
|
-}
|
|
|
-
|
|
|
-.bar-btn-preview {
|
|
|
- width: 200rpx;
|
|
|
- background: #fff;
|
|
|
- color: #52c41a;
|
|
|
- border: 2rpx solid #ddd;
|
|
|
- margin-right: 16rpx;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- line-height: 1;
|
|
|
-}
|
|
|
-
|
|
|
-.preview-eye {
|
|
|
- margin-right: 6rpx;
|
|
|
- font-size: 28rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.bar-btn-save {
|
|
|
- flex: 1;
|
|
|
- background: #52c41a;
|
|
|
- color: #fff;
|
|
|
-}
|
|
|
-
|
|
|
-.picker-overlay {
|
|
|
- position: fixed;
|
|
|
- left: 0;
|
|
|
- top: 0;
|
|
|
- right: 0;
|
|
|
- bottom: 0;
|
|
|
- z-index: 200;
|
|
|
- display: flex;
|
|
|
- align-items: flex-end;
|
|
|
- justify-content: center;
|
|
|
-}
|
|
|
-
|
|
|
-.picker-mask {
|
|
|
- position: absolute;
|
|
|
- left: 0;
|
|
|
- top: 0;
|
|
|
- right: 0;
|
|
|
- bottom: 0;
|
|
|
- background: rgba(0, 0, 0, 0.5);
|
|
|
-}
|
|
|
-
|
|
|
-.picker-panel {
|
|
|
- position: relative;
|
|
|
- z-index: 1;
|
|
|
- width: 100%;
|
|
|
- max-height: 70vh;
|
|
|
- background: #fff;
|
|
|
- border-radius: 24rpx 24rpx 0 0;
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
-}
|
|
|
-
|
|
|
-.category-picker-panel {
|
|
|
- max-height: 82vh;
|
|
|
-}
|
|
|
-
|
|
|
-.picker-head {
|
|
|
- position: relative;
|
|
|
- height: 88rpx;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- border-bottom: 1rpx solid #f0f0f0;
|
|
|
- flex-shrink: 0;
|
|
|
-}
|
|
|
-
|
|
|
-.picker-title {
|
|
|
- font-size: 32rpx;
|
|
|
- font-weight: 600;
|
|
|
- color: #333;
|
|
|
-}
|
|
|
-
|
|
|
-.picker-count {
|
|
|
- position: absolute;
|
|
|
- left: 24rpx;
|
|
|
- font-size: 24rpx;
|
|
|
- color: #52c41a;
|
|
|
-}
|
|
|
-
|
|
|
-.picker-close {
|
|
|
- position: absolute;
|
|
|
- right: 24rpx;
|
|
|
- font-size: 36rpx;
|
|
|
- color: #999;
|
|
|
- padding: 8rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.picker-search-row {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- padding: 16rpx 24rpx;
|
|
|
- border-bottom: 1rpx solid #f5f5f5;
|
|
|
- flex-shrink: 0;
|
|
|
-}
|
|
|
-
|
|
|
-.picker-search-box {
|
|
|
- flex: 1;
|
|
|
- min-width: 0;
|
|
|
- height: 68rpx;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- padding: 0 20rpx;
|
|
|
- border: 2rpx solid #e8e8e8;
|
|
|
- border-radius: 34rpx;
|
|
|
- background: #fafafa;
|
|
|
- box-sizing: border-box;
|
|
|
-}
|
|
|
-
|
|
|
-.picker-search-icon {
|
|
|
- font-size: 28rpx;
|
|
|
- color: #999;
|
|
|
- margin-right: 12rpx;
|
|
|
- flex-shrink: 0;
|
|
|
-}
|
|
|
-
|
|
|
-.picker-search-input {
|
|
|
- flex: 1;
|
|
|
- min-width: 0;
|
|
|
- height: 64rpx;
|
|
|
- font-size: 26rpx;
|
|
|
- color: #333;
|
|
|
-}
|
|
|
-
|
|
|
-.picker-search-clear {
|
|
|
- margin-left: 16rpx;
|
|
|
- font-size: 24rpx;
|
|
|
- color: #52c41a;
|
|
|
- flex-shrink: 0;
|
|
|
-}
|
|
|
-
|
|
|
-.picker-body {
|
|
|
- flex: 1;
|
|
|
- min-height: 0;
|
|
|
- max-height: 46vh;
|
|
|
- padding: 8rpx 24rpx 16rpx;
|
|
|
- box-sizing: border-box;
|
|
|
-}
|
|
|
-
|
|
|
-.picker-empty {
|
|
|
- padding: 80rpx 0;
|
|
|
- text-align: center;
|
|
|
- font-size: 26rpx;
|
|
|
- color: #999;
|
|
|
-}
|
|
|
-
|
|
|
-.picker-item {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- padding: 22rpx 0;
|
|
|
- border-bottom: 1rpx solid #f5f5f5;
|
|
|
-}
|
|
|
-
|
|
|
-.picker-item:last-child {
|
|
|
- border-bottom: none;
|
|
|
-}
|
|
|
-
|
|
|
-.check-box {
|
|
|
- width: 36rpx;
|
|
|
- height: 36rpx;
|
|
|
- border: 2rpx solid #ccc;
|
|
|
- border-radius: 6rpx;
|
|
|
- margin-right: 16rpx;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- flex-shrink: 0;
|
|
|
-}
|
|
|
-
|
|
|
-.check-box.checked {
|
|
|
- background: #52c41a;
|
|
|
- border-color: #52c41a;
|
|
|
-}
|
|
|
-
|
|
|
-.check-mark {
|
|
|
- color: #fff;
|
|
|
- font-size: 24rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.picker-item-name {
|
|
|
- flex: 1;
|
|
|
- min-width: 0;
|
|
|
- font-size: 28rpx;
|
|
|
- color: #333;
|
|
|
- line-height: 1.4;
|
|
|
-}
|
|
|
-
|
|
|
-.picker-footer {
|
|
|
- display: flex;
|
|
|
- padding: 20rpx 24rpx;
|
|
|
- padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
|
|
- flex-shrink: 0;
|
|
|
- border-top: 1rpx solid #f0f0f0;
|
|
|
-}
|
|
|
-
|
|
|
-.picker-btn {
|
|
|
- flex: 1;
|
|
|
- height: 80rpx;
|
|
|
- line-height: 80rpx;
|
|
|
- margin: 0;
|
|
|
- font-size: 28rpx;
|
|
|
- border-radius: 8rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.picker-btn::after {
|
|
|
- border: none;
|
|
|
-}
|
|
|
-
|
|
|
-.picker-btn.cancel {
|
|
|
- margin-right: 20rpx;
|
|
|
- background: #fff;
|
|
|
- color: #333;
|
|
|
- border: 2rpx solid #ddd;
|
|
|
-}
|
|
|
-
|
|
|
-.picker-btn.confirm {
|
|
|
- background: #52c41a;
|
|
|
- color: #fff;
|
|
|
-}
|
|
|
-
|
|
|
-.preview-panel {
|
|
|
- position: relative;
|
|
|
- z-index: 1;
|
|
|
- width: 90%;
|
|
|
- background: #fff;
|
|
|
- border-radius: 16rpx;
|
|
|
- padding: 24rpx;
|
|
|
- box-sizing: border-box;
|
|
|
-}
|
|
|
-
|
|
|
-.preview-swiper {
|
|
|
- height: 320rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.preview-img {
|
|
|
- width: 100%;
|
|
|
- height: 320rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.preview-close {
|
|
|
- margin-top: 20rpx;
|
|
|
- height: 72rpx;
|
|
|
- line-height: 72rpx;
|
|
|
- background: #52c41a;
|
|
|
- color: #fff;
|
|
|
- font-size: 28rpx;
|
|
|
- border-radius: 8rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.preview-close::after {
|
|
|
- border: none;
|
|
|
-}
|
|
|
-</style>
|