945cf9c104
e192296 把纯白透明底图片图标垫了深色底块反衬,解决了「白底上看不见」,
但本质是靠底色绕开图源问题,选中态也就只能靠加深底色表达。
改用 u-icon 矢量图标后可直接着色:
- 首页 tab:未选中灰色 #6b7280,选中白色;图标按语义取
order / clock / checkmark-circle
- 自主填报入口:未选中浅绿底 + 绿色图标,选中深绿底 + 白色图标;
store/visit 取 home-fill / car-fill
顺带删掉两处为图片补正尺寸的样式(.tab-icon-wrap 白圆底、
.type-icon/.type-icon-store 的宽高补正),矢量图标不需要。
图标名已对照 uview 图标库校验(u-icon 会自动补 uicon- 前缀)。
1447 lines
36 KiB
Vue
1447 lines
36 KiB
Vue
<template>
|
||
<view class="home-page">
|
||
<view class="top-wrap">
|
||
<AppNavbar class="top-navbar" title="药小二" title-size="28rpx" gap="40rpx"/>
|
||
|
||
<view class="search-bar top-search">
|
||
<u-search showAction animation v-model="searchQuery" bgColor="white"
|
||
searchIcon="https://yaodongxiao.oss-cn-guangzhou.aliyuncs.com/20260126/16f54dd102cacccfb5085533009b70fc.png"/>
|
||
</view>
|
||
|
||
<view v-if="bannersEnabled && banners.length" class="banner-area">
|
||
<swiper class="banner-swiper" autoplay :interval="4200" indicator-dots indicator-color="#bfeee4"
|
||
indicator-active-color="#1ac5a6">
|
||
<swiper-item v-for="item in banners" :key="item.id">
|
||
<image class="banner-img" :src="item.image" mode="aspectFill"/>
|
||
</swiper-item>
|
||
</swiper>
|
||
</view>
|
||
<!-- <view class="notice-bar">-->
|
||
<!-- <text class="notice-label">奖励:</text>-->
|
||
<!-- <swiper class="notice-swiper" vertical circular autoplay :interval="3200" :duration="400">-->
|
||
<!-- <swiper-item v-for="(notice, idx) in notices" :key="idx" class="notice-item">-->
|
||
<!-- <text class="notice-text">{{ notice }}</text>-->
|
||
<!-- </swiper-item>-->
|
||
<!-- </swiper>-->
|
||
<!-- <view class="notice-more">-->
|
||
<!-- <text class="notice-more-icon">></text>-->
|
||
<!-- </view>-->
|
||
<!-- </view>-->
|
||
<view class="self-report-entry">
|
||
<view class="entry-btn primary" @tap="openSelfReportCreate">
|
||
<view class="entry-icon">
|
||
<u-icon name="edit-pen" size="26" color="#f97316"/>
|
||
</view>
|
||
<view class="entry-text">
|
||
<text class="entry-title">自主填报</text>
|
||
<text class="entry-desc">便捷填报,快速提交</text>
|
||
</view>
|
||
</view>
|
||
<view class="entry-btn" @tap="openSelfReportRecord">
|
||
<view class="entry-icon">
|
||
<u-icon name="order" size="26" color="#f97316"/>
|
||
</view>
|
||
<view class="entry-text">
|
||
<text class="entry-title">填报记录</text>
|
||
<text class="entry-desc">历史记录,随时查看</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="nav-wrap">
|
||
<view class="tabs">
|
||
<view v-for="tab in tabs" :key="tab" :class="['tab', activeTab === tab ? 'active' : '']"
|
||
@tap="activeTab = tab">
|
||
<view class="tab-main">
|
||
<u-icon :name="tabIcon(tab)" size="15"
|
||
:color="activeTab === tab ? '#ffffff' : '#6b7280'"/>
|
||
<text class="tab-text">{{ tab }}</text>
|
||
</view>
|
||
<view v-if="tab === '待执行' && pendingCount" class="tab-badge">{{ pendingCount }}</view>
|
||
<view v-if="activeTab === tab" class="tab-underline"/>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="category-wrap">
|
||
<scroll-view class="category-bar" scroll-x show-scrollbar="false">
|
||
<view v-for="cat in categories" :key="cat" :class="['category-chip', category === cat.id ? 'active' : '']"
|
||
@tap="changeCate(cat.id)">
|
||
{{ cat.title }}
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
</view>
|
||
<view class="board">
|
||
<view class="filter-row">
|
||
<view class="location" @tap="toggleCity">
|
||
<u-icon name="map"/>
|
||
<text>{{ currentCity }}</text>
|
||
</view>
|
||
<view class="sort-options">
|
||
<view v-for="sort in sortOptions" :key="sort" :class="['sort-item', sortType === sort ? 'active' : '']"
|
||
@tap="sortType = sort">
|
||
<text class="sort-text">{{ sort }}</text>
|
||
<text class="sort-arrow">▼</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="task-list">
|
||
<view v-if="getFilteredTasks().length === 0" class="empty">
|
||
<u-empty
|
||
icon="https://yaodongxiao.oss-cn-guangzhou.aliyuncs.com/20260126/ddcc1bbd561d97128ed19407b07e299b.png"
|
||
text="暂无符合条件的任务"/>
|
||
</view>
|
||
<view v-for="task in getFilteredTasks()" :key="task.id" class="task-card">
|
||
<view class="task-head">
|
||
<view class="date-row">
|
||
<text class="date">{{ formatTaskDateText(task) }}</text>
|
||
<view class="relative">{{ formatTaskDateSubText(task) }}</view>
|
||
<view v-if="task.cate && task.cate.title" class="cate-tag">{{ task.cate.title }}</view>
|
||
</view>
|
||
<view v-if="!isManualSalesTask(task)" class="reward">
|
||
<text class="currency">¥</text>
|
||
<text class="reward-number">{{ calReward(task) }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="task-title-row">
|
||
<text class="task-title">{{ task.merchant.name }}</text>
|
||
<view v-if="task.chainText" class="chain-pill">{{ task.chainText }}</view>
|
||
<view :class="['type-pill', 'type-blue']">{{ task.cate.title }}</view>
|
||
</view>
|
||
<text class="addr">{{ task.title }}</text>
|
||
<view class="task-meta" v-if="task.merchant">
|
||
<text class="addr">{{ task.merchant.province }}{{ task.merchant.city }}{{
|
||
task.merchant.region
|
||
}}{{ task.merchant.address }}
|
||
</text>
|
||
<view class="distance">
|
||
<u-icon name="map"/>
|
||
<text>{{ task.distance }}</text>
|
||
</view>
|
||
</view>
|
||
<view v-if="task.userTask" class="total-progress-card">
|
||
<view class="total-progress-head">
|
||
<text class="total-progress-title">总进度</text>
|
||
<text class="total-progress-value">{{ totalProgressText(task) }}</text>
|
||
</view>
|
||
<view class="total-progress-bar">
|
||
<view class="total-progress-fill" :style="{ width: `${totalProgressPercent(task)}%` }"></view>
|
||
</view>
|
||
</view>
|
||
<view v-if="task.userTask && [1, 2, 3].includes(task.userTask.status)" class="audit-box">
|
||
<view class="flex flex-col justify-start items-start">
|
||
<view :class="['audit-status', auditClass(task.userTask.status)]">订单状态:
|
||
{{ auditLabel(task.userTask.status) }}
|
||
</view>
|
||
<text v-if="task.userTask.auditReason" class="audit-reason">原因: {{ task.userTask.auditReason }}</text>
|
||
</view>
|
||
<view class="info-action" @tap="showDetails(task)">查看详情</view>
|
||
</view>
|
||
<view class="actions" v-if="task.status === 0">
|
||
<view class="ghost-action" @tap="openDetailPopup(task)">查看详情</view>
|
||
<view class="primary-action" @tap="takeOver(task)">接单</view>
|
||
<!-- <view v-if="task.status === 'pending'" class="primary-action full" @tap="goExecute(task)">去执行</view>-->
|
||
</view>
|
||
<view class="actions" v-if="task.status === 1 && task.userTask && task.userTask.status === 0">
|
||
<view class="ghost-action" @tap="openDetailPopup(task)">查看详情</view>
|
||
<view class="action-btn" @tap="showDetails(task)">去执行</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<u-popup :show="detailPopupVisible" mode="bottom" round="36rpx" close-on-click-overlay @close="closeDetailPopup">
|
||
<view class="detail-popup" v-if="detailTask">
|
||
<view class="detail-popup-head">
|
||
<text class="detail-popup-title">任务详情</text>
|
||
<view class="detail-popup-close" @tap="closeDetailPopup">
|
||
<u-icon name="close" size="36rpx" color="#7a8495"/>
|
||
</view>
|
||
</view>
|
||
<view class="detail-merchant-card">
|
||
<view v-if="detailTask.chainText" class="detail-merchant-row">
|
||
<text class="detail-merchant-label">所属连锁</text>
|
||
<text class="detail-merchant-value">{{ detailTask.chainText }}</text>
|
||
</view>
|
||
<view v-if="detailTask.merchant && detailTask.merchant.name" class="detail-merchant-row">
|
||
<text class="detail-merchant-label">任务门店</text>
|
||
<text class="detail-merchant-value">{{ detailTask.merchant.name }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="detail-requirement">
|
||
<view class="detail-block-head">
|
||
<view class="detail-icon">
|
||
<u-icon
|
||
name="https://yaodongxiao.oss-cn-guangzhou.aliyuncs.com/20260127/a6a3db8692c2366cf3d219b8d5009b7d.png"
|
||
size="34rpx"/>
|
||
</view>
|
||
<text class="detail-block-title">任务要求</text>
|
||
</view>
|
||
<text class="detail-block-text">{{ detailTask.detail }}</text>
|
||
</view>
|
||
<!-- <view class="detail-section" v-if="detailTask.cateId !== 4">-->
|
||
<!-- <view class="detail-section-title">-->
|
||
<!-- <view class="detail-section-dot orange"></view>-->
|
||
<!-- <text>重点爆破品种</text>-->
|
||
<!-- </view>-->
|
||
<!-- <view class="detail-section-card">{{-->
|
||
<!-- detailTask.productList.map(item => item.product.title).join('、')-->
|
||
<!-- }}-->
|
||
<!-- </view>-->
|
||
<!-- </view>-->
|
||
<view class="detail-section">
|
||
<view class="detail-section-title">
|
||
<view class="detail-section-dot green"></view>
|
||
<text>活动政策</text>
|
||
</view>
|
||
<view class="detail-section-card">{{ detailTask.policy }}</view>
|
||
</view>
|
||
<view class="detail-section">
|
||
<view class="detail-section-title">
|
||
<view class="detail-section-dot blue"></view>
|
||
<text>任务执行时间段</text>
|
||
</view>
|
||
<view class="detail-section-card">{{ formatTaskExecutePeriod(detailTask) }}</view>
|
||
</view>
|
||
</view>
|
||
</u-popup>
|
||
|
||
<AreaPicker
|
||
:show="showAreaPicker"
|
||
:default-province="currentProvince"
|
||
:default-city="currentCity"
|
||
@close="showAreaPicker = false"
|
||
@confirm="onAreaConfirm"
|
||
/>
|
||
|
||
<Login ref="Login" @done="handleLoginDone"/>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import {addUserTaskReq, taskCateListReq, taskDoneListReq, taskListReq} from "@/api/task";
|
||
import dayjs from "dayjs";
|
||
import relativeTime from "dayjs/plugin/relativeTime";
|
||
import "dayjs/locale/zh-cn";
|
||
import {calcDistanceMeters, formatDistance, getLocationAction} from "@/util";
|
||
import {swiperListReq} from "@/api/common";
|
||
import AppNavbar from "@/components/AppNavbar.vue";
|
||
import AreaPicker from "@/components/AreaPicker.vue";
|
||
import Login from "@/components/Login.vue";
|
||
import {buildSharePath, getUserInfo} from "@/util/user";
|
||
|
||
dayjs.extend(relativeTime);
|
||
dayjs.locale("zh-cn");
|
||
|
||
export default {
|
||
components: {AppNavbar, AreaPicker, Login},
|
||
data() {
|
||
return {
|
||
bannersEnabled: true,
|
||
banners: [],
|
||
tabs: ['新任务', '待执行', '已完成'],
|
||
activeTab: '待执行',
|
||
searchQuery: '',
|
||
categories: [],
|
||
category: null,
|
||
sortOptions: ['综合排序', '距我最近', '热度最高'],
|
||
sortType: '综合排序',
|
||
currentCity: '定位中',
|
||
currentProvince: '',
|
||
showAreaPicker: false,
|
||
notices: [
|
||
// '李强已完成业绩 ¥12,000',
|
||
// '黄敏新接单 3 个门店任务',
|
||
// '周杰完成活动爆破业绩 ¥18,500',
|
||
// '团队今日累计业绩 ¥65,300'
|
||
],
|
||
tasks: [],
|
||
lat: null,
|
||
lng: null,
|
||
detailPopupVisible: false,
|
||
detailTask: null,
|
||
pendingLoginUrl: ''
|
||
}
|
||
},
|
||
computed: {
|
||
activeStatus() {
|
||
if (this.activeTab === '新任务') return 'new';
|
||
if (this.activeTab === '待执行') return 'pending';
|
||
return 'completed';
|
||
},
|
||
pendingCount() {
|
||
return this.tasks.filter(t => t.status === 'pending').length;
|
||
},
|
||
},
|
||
watch: {
|
||
activeTab() {
|
||
this.getTaskList()
|
||
}
|
||
},
|
||
async onShow() {
|
||
await this.refreshPageData()
|
||
},
|
||
async onPullDownRefresh() {
|
||
try {
|
||
await this.refreshPageData()
|
||
} finally {
|
||
uni.stopPullDownRefresh()
|
||
}
|
||
},
|
||
methods: {
|
||
// tab 图标:新任务=任务清单、待执行=时钟、已完成=对勾
|
||
tabIcon(tab) {
|
||
if (tab === '新任务') return 'order'
|
||
if (tab === '待执行') return 'clock'
|
||
return 'checkmark-circle'
|
||
},
|
||
async refreshPageData() {
|
||
const res = await getLocationAction().catch(() => {
|
||
uni.showToast({
|
||
title: '请允许位置授权',
|
||
icon: 'none'
|
||
})
|
||
})
|
||
if (res) {
|
||
this.lat = res.latitude
|
||
this.lng = res.longitude
|
||
this.currentCity = res.city_str
|
||
this.currentProvince = res.province_str || ''
|
||
this.updateTaskDistances()
|
||
}
|
||
await this.getSwiperList()
|
||
await this.getCateList()
|
||
await this.getNoticeList()
|
||
await this.getTaskList()
|
||
},
|
||
async getNoticeList() {
|
||
const {data} = await taskDoneListReq()
|
||
this.notices = data.map(item => {
|
||
return `${item.user.nickname}已完成业绩 ¥${item.reward}`
|
||
})
|
||
},
|
||
async getSwiperList() {
|
||
const {data} = await swiperListReq()
|
||
this.banners = data
|
||
},
|
||
async getCateList() {
|
||
const {data} = await taskCateListReq({
|
||
filter: true
|
||
});
|
||
this.categories = [{id: null, title: '全部'}, ...data]
|
||
},
|
||
async getTaskList() {
|
||
const params = {
|
||
province: this.currentProvince,
|
||
city: this.currentCity,
|
||
notExpired: true
|
||
}
|
||
if (this.category) params.cateId = this.category;
|
||
params.userTaskStatus = this.getUserTaskStatus()
|
||
const {data} = await taskListReq(params);
|
||
this.tasks = data.map(task => Object.assign({}, task, {
|
||
chainText: this.getTaskChainText(task)
|
||
}))
|
||
this.updateTaskDistances()
|
||
},
|
||
changeCate(id) {
|
||
this.category = id;
|
||
this.getTaskList()
|
||
},
|
||
toggleCity() {
|
||
this.showAreaPicker = true
|
||
},
|
||
onAreaConfirm({provinceName, cityName}) {
|
||
this.currentProvince = provinceName
|
||
this.currentCity = cityName
|
||
this.showAreaPicker = false
|
||
this.getTaskList()
|
||
},
|
||
getUserTaskStatus() {
|
||
if (this.activeTab === '新任务') return -1
|
||
if (this.activeTab === '待执行') return 0
|
||
return 1
|
||
},
|
||
distanceToNumber(distance) {
|
||
if (!distance) return 0;
|
||
if (distance.indexOf('km') > -1) return parseFloat(distance);
|
||
if (distance.indexOf('m') > -1) return parseFloat(distance) / 1000;
|
||
return parseFloat(distance);
|
||
},
|
||
formatRelativeDate(value) {
|
||
if (!value) return '';
|
||
const date = dayjs(value);
|
||
return date.isValid() ? date.fromNow() : value;
|
||
},
|
||
formatTaskDateText(task) {
|
||
const isPendingTab = this.activeTab === '待执行'
|
||
const isNewTab = this.activeTab === '新任务'
|
||
const startTime = task && task.taskStartTime ? task.taskStartTime : ''
|
||
const taskDate = task && task.date ? task.date : ''
|
||
if (isPendingTab && startTime) {
|
||
const date = dayjs(startTime)
|
||
if (date.isValid()) return date.format('MM月DD日')
|
||
}
|
||
if (isNewTab && taskDate) {
|
||
const date = dayjs(taskDate)
|
||
if (date.isValid()) return date.format('MM月DD日')
|
||
}
|
||
return task && task.date ? task.date : ''
|
||
},
|
||
formatTaskDateSubText(task) {
|
||
const isPendingTab = this.activeTab === '待执行'
|
||
const isNewTab = this.activeTab === '新任务'
|
||
const startTime = task && task.taskStartTime ? task.taskStartTime : ''
|
||
const taskDate = task && task.date ? task.date : ''
|
||
if (isPendingTab && startTime) {
|
||
const date = dayjs(startTime)
|
||
if (date.isValid()) {
|
||
const weekdays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
|
||
const weekday = weekdays[date.day()] || ''
|
||
const daysAfter = this.getDaysAfterToday(date)
|
||
const daysText = daysAfter < 0 ? '已过期' : (daysAfter === 0 ? '今天' : `${daysAfter}天后`)
|
||
return `${weekday} ${daysText}`.trim()
|
||
}
|
||
}
|
||
if (isNewTab && taskDate) {
|
||
const date = dayjs(taskDate)
|
||
if (date.isValid()) {
|
||
const weekdays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
|
||
return weekdays[date.day()] || ''
|
||
}
|
||
}
|
||
return this.formatRelativeDate(task && task.date ? task.date : '')
|
||
},
|
||
getDaysAfterToday(dateValue) {
|
||
const target = dayjs(dateValue).startOf('day')
|
||
if (!target.isValid()) return 0
|
||
const today = dayjs().startOf('day')
|
||
return target.diff(today, 'day')
|
||
},
|
||
updateTaskDistances() {
|
||
if (!Number.isFinite(this.lat) || !Number.isFinite(this.lng)) return;
|
||
this.tasks = this.tasks.map(task => {
|
||
const merchant = task.merchant || {};
|
||
const lat = Number(merchant.lat);
|
||
const lng = Number(merchant.lng);
|
||
if (!Number.isFinite(lat) || !Number.isFinite(lng)) return task;
|
||
const meters = calcDistanceMeters(this.lat, this.lng, lat, lng);
|
||
return Object.assign({}, task, {
|
||
distance: formatDistance(meters)
|
||
});
|
||
});
|
||
},
|
||
getFilteredTasks(status) {
|
||
let list = this.tasks;
|
||
if (this.category) {
|
||
list = list.filter(item => item.cateId === this.category);
|
||
}
|
||
if (this.searchQuery) {
|
||
const keyword = this.searchQuery.trim();
|
||
list = list.filter(item => item.merchant.name.indexOf(keyword) > -1 || item.title.indexOf(keyword) > -1);
|
||
}
|
||
if (this.sortType === '热度最高') {
|
||
list = [...list].sort((a, b) => this.rewardToNumber(b) - this.rewardToNumber(a));
|
||
}
|
||
if (this.sortType === '距我最近') {
|
||
list = [...list].sort((a, b) => this.distanceToNumber(a.distance) - this.distanceToNumber(b.distance));
|
||
}
|
||
return list;
|
||
},
|
||
calReward(item) {
|
||
let reward = parseFloat(item.reward)
|
||
if (item.extraReward) reward += parseFloat(item.extraReward)
|
||
return reward.toFixed(2)
|
||
},
|
||
rewardToNumber(item) {
|
||
if (this.isManualSalesTask(item)) return 0;
|
||
const reward = Number(item && item.reward);
|
||
const extraReward = Number(item && item.extraReward);
|
||
return (Number.isFinite(reward) ? reward : 0) + (Number.isFinite(extraReward) ? extraReward : 0);
|
||
},
|
||
auditLabel(status) {
|
||
if (status === 2) return '审核通过';
|
||
if (status === 3) return '审核不通过';
|
||
return '待审核';
|
||
},
|
||
auditClass(status) {
|
||
if (status === 2) return 'audit-success';
|
||
if (status === 3) return 'audit-danger';
|
||
return 'audit-warn';
|
||
},
|
||
parseJsonMaybe(value) {
|
||
if (value === null || value === undefined || value === '') return null;
|
||
if (typeof value === 'string') {
|
||
try {
|
||
return JSON.parse(value);
|
||
} catch (err) {
|
||
return value;
|
||
}
|
||
}
|
||
return value;
|
||
},
|
||
safeNumber(value) {
|
||
const num = Number(value);
|
||
return Number.isFinite(num) ? num : 0;
|
||
},
|
||
getTaskProductList(task) {
|
||
const raw = task && task.productList ? task.productList : [];
|
||
const parsed = this.parseJsonMaybe(raw);
|
||
return Array.isArray(parsed) ? parsed : [];
|
||
},
|
||
getTaskSalesGroups(task) {
|
||
const userTask = task && task.userTask ? task.userTask : {};
|
||
const raw = userTask.salesGroups || userTask.sales_groups || task.salesGroups || task.sales_groups || [];
|
||
const parsed = this.parseJsonMaybe(raw);
|
||
return Array.isArray(parsed) ? parsed : [];
|
||
},
|
||
calcTotalTaskNum(task) {
|
||
const list = this.getTaskProductList(task);
|
||
if (list.length) {
|
||
return list.reduce((total, item) => {
|
||
const raw = item ? (item.num || item.taskNum || item.quantity || item.count || item.targetNum) : 0;
|
||
return total + this.safeNumber(raw);
|
||
}, 0);
|
||
}
|
||
const map = new Map();
|
||
const groups = this.getTaskSalesGroups(task);
|
||
groups.forEach(group => {
|
||
const items = Array.isArray(group.items) ? group.items : [];
|
||
items.forEach(item => {
|
||
if (!item || !item.taskNum) return;
|
||
const key = item.productId ? `id:${item.productId}` : `name:${item.name || ''}`;
|
||
if (!key) return;
|
||
const current = map.get(key) || 0;
|
||
map.set(key, Math.max(current, this.safeNumber(item.taskNum)));
|
||
});
|
||
});
|
||
let total = 0;
|
||
map.forEach(value => {
|
||
total += value;
|
||
});
|
||
return total;
|
||
},
|
||
calcTotalQty(task) {
|
||
if (task.cateId === 4) {
|
||
return task.userTask.storeList ? JSON.parse(task.userTask.storeList).length : 0;
|
||
}
|
||
const groups = this.getTaskSalesGroups(task);
|
||
return groups.reduce((total, group) => {
|
||
const items = Array.isArray(group.items) ? group.items : [];
|
||
const sum = items.reduce((acc, item) => acc + this.safeNumber(item ? item.qty : 0), 0);
|
||
return total + sum;
|
||
}, 0);
|
||
},
|
||
totalProgressPercent(task) {
|
||
let totalTaskNum = this.calcTotalTaskNum(task);
|
||
if (task.cateId === 4) {
|
||
totalTaskNum = task.storeNum
|
||
}
|
||
if (!totalTaskNum) return 0;
|
||
const totalQty = this.calcTotalQty(task);
|
||
return Math.min(100, Math.round((totalQty / totalTaskNum) * 100));
|
||
},
|
||
totalProgressText(task) {
|
||
let totalTaskNum = this.calcTotalTaskNum(task);
|
||
if (task.cateId === 4) {
|
||
totalTaskNum = task.storeNum
|
||
}
|
||
const totalQty = this.calcTotalQty(task);
|
||
return `${totalQty}/${totalTaskNum || 0}`;
|
||
},
|
||
isManualSalesTask(task) {
|
||
if (!task || task.cateId === 4) return false;
|
||
const list = Array.isArray(task.productList) ? task.productList : [];
|
||
return list.length === 0;
|
||
},
|
||
getTaskChainText(task) {
|
||
const merchant = task && task.merchant ? task.merchant : {};
|
||
const chain = merchant.chain || merchant.chainName || merchant.chainAbb || task.chain || task.chainName || '';
|
||
return String(chain || '').trim();
|
||
},
|
||
showDetails(task) {
|
||
let str = `reward=${task.reward}&target=${task.cate.title}&id=${task.id}`
|
||
if (task.merchant) str += `&title=${encodeURIComponent(task.merchant.name)}`
|
||
uni.navigateTo({
|
||
url: `/pages/task/detail?${str}`
|
||
})
|
||
},
|
||
async takeOver(task) {
|
||
const res = await addUserTaskReq({
|
||
taskId: task.id
|
||
}).catch(e => {
|
||
uni.showToast({
|
||
title: e.message,
|
||
icon: 'none'
|
||
})
|
||
})
|
||
if (res) {
|
||
uni.showToast({
|
||
title: '接单成功',
|
||
icon: 'none'
|
||
})
|
||
this.activeTab = '待执行'
|
||
}
|
||
},
|
||
openDetailPopup(task) {
|
||
this.detailTask = task ? Object.assign({}, task, {
|
||
chainText: task.chainText || this.getTaskChainText(task)
|
||
}) : {};
|
||
this.detailPopupVisible = true;
|
||
},
|
||
openSelfReportCreate() {
|
||
// 「自主填报」先选填报类型(驻店填报 / 跑店填报),再进对应表单页
|
||
this.jumpWithLogin('/packageUser/self-report/select')
|
||
},
|
||
openSelfReportRecord() {
|
||
this.jumpWithLogin('/packageUser/self-report/records')
|
||
},
|
||
jumpWithLogin(url) {
|
||
if (!getUserInfo().token) {
|
||
this.pendingLoginUrl = url
|
||
this.$refs.Login.open()
|
||
return
|
||
}
|
||
uni.navigateTo({url})
|
||
},
|
||
handleLoginDone() {
|
||
if (!this.pendingLoginUrl) return
|
||
const url = this.pendingLoginUrl
|
||
this.pendingLoginUrl = ''
|
||
uni.navigateTo({url})
|
||
},
|
||
closeDetailPopup() {
|
||
this.detailPopupVisible = false;
|
||
},
|
||
trimSecond(value) {
|
||
const text = value === null || value === undefined ? '' : String(value).trim()
|
||
if (!text) return ''
|
||
return text.replace(/(\d{2}:\d{2}):\d{2}\b/, '$1')
|
||
},
|
||
formatTaskExecutePeriod(task) {
|
||
const source = task || {}
|
||
const start = this.trimSecond(source.taskStartTime || '')
|
||
const end = this.trimSecond(source.taskEndTime || '')
|
||
if (start && end) return `${start} 至 ${end}`
|
||
if (start) return `${start} 至 -`
|
||
if (end) return `- 至 ${end}`
|
||
return '暂无时间段'
|
||
},
|
||
},
|
||
onShareTimeline() {
|
||
return {
|
||
title: '药动销'
|
||
}
|
||
},
|
||
onShareAppMessage() {
|
||
return {
|
||
title: '药动销',
|
||
path: buildSharePath('/pages/index/index')
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.home-page {
|
||
background: #f5f5f5;
|
||
min-height: 100vh;
|
||
padding-bottom: 40rpx;
|
||
}
|
||
|
||
.top-wrap {
|
||
position: relative;
|
||
padding: 0 0 32rpx;
|
||
}
|
||
|
||
.top-navbar {
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.top-search {
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.search-bar {
|
||
width: calc(100vw - 70rpx);
|
||
z-index: 9999;
|
||
position: absolute;
|
||
display: flex;
|
||
align-items: center;
|
||
background: #ffffff;
|
||
border-radius: 999rpx;
|
||
padding: 5rpx 18rpx;
|
||
margin: 0 20rpx;
|
||
top: 170rpx;
|
||
box-shadow: 0 8rpx 18rpx rgba(0, 0, 0, 0.06);
|
||
}
|
||
|
||
.search-icon {
|
||
width: 32rpx;
|
||
height: 32rpx;
|
||
margin-right: 12rpx;
|
||
}
|
||
|
||
.search-btn {
|
||
font-size: 26rpx;
|
||
font-weight: 700;
|
||
color: #111827;
|
||
padding: 6rpx 12rpx;
|
||
width: calc(100% - 40rpx);
|
||
}
|
||
|
||
.banner-area {
|
||
padding: 0;
|
||
overflow: hidden;
|
||
border-radius: 0;
|
||
//box-shadow: 0 12rpx 24rpx rgba(0, 0, 0, 0.08);
|
||
}
|
||
|
||
.banner-swiper {
|
||
height: 320rpx;
|
||
}
|
||
|
||
.banner-img {
|
||
width: 100%;
|
||
height: 320rpx;
|
||
display: block;
|
||
}
|
||
|
||
.board {
|
||
margin-top: -5rpx;
|
||
background: #f5f5f5;
|
||
border-radius: 28rpx 28rpx 0 0;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.tabs {
|
||
display: flex;
|
||
padding: 10rpx 24rpx 18rpx;
|
||
//background: #f3fffb;
|
||
gap: 12rpx;
|
||
}
|
||
|
||
.tab {
|
||
flex: 1;
|
||
text-align: center;
|
||
padding: 12rpx 0;
|
||
color: #6b7280;
|
||
font-weight: 700;
|
||
position: relative;
|
||
font-size: 30rpx;
|
||
}
|
||
|
||
.tab-main {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 8rpx;
|
||
padding: 10rpx 18rpx;
|
||
border-radius: 999rpx;
|
||
}
|
||
|
||
.tab-text {
|
||
line-height: 1;
|
||
}
|
||
|
||
/* 三个 tab 原先用纯白 PNG 图标,而 tab 底图接近纯白(平均亮度 250/255),
|
||
未激活时白图标贴白底完全看不清;且原图语义也不对(「新任务」是 NEW 徽章、
|
||
「待执行」是时钟)。改用 uview 矢量图标,可随激活态着色,无底色依赖。 */
|
||
.tab-icon {
|
||
width: 28rpx;
|
||
height: 28rpx;
|
||
}
|
||
|
||
.tab.active .tab-main {
|
||
background: #2fc6a7;
|
||
color: #ffffff;
|
||
}
|
||
|
||
.tab-badge {
|
||
position: absolute;
|
||
top: 4rpx;
|
||
right: 16rpx;
|
||
min-width: 30rpx;
|
||
padding: 4rpx 10rpx;
|
||
background: #ff3b30;
|
||
color: #fff;
|
||
border-radius: 999rpx;
|
||
font-size: 20rpx;
|
||
border: 2rpx solid #ffffff;
|
||
}
|
||
|
||
.tab-underline {
|
||
position: absolute;
|
||
bottom: 3rpx;
|
||
left: 50%;
|
||
width: 16rpx;
|
||
height: 16rpx;
|
||
background: #2fc6a7;
|
||
transform: translateX(-50%) rotate(45deg);
|
||
border-radius: 4rpx;
|
||
}
|
||
|
||
.tab.active {
|
||
color: #d9661f;
|
||
}
|
||
|
||
.search-input {
|
||
flex: 1;
|
||
font-size: 26rpx;
|
||
color: #0f2f52;
|
||
}
|
||
|
||
.search-placeholder {
|
||
color: #a1aabc;
|
||
}
|
||
|
||
.category-bar {
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.notice-bar {
|
||
margin: 20rpx 24rpx 10rpx;
|
||
background: #ffffff;
|
||
border-radius: 999rpx;
|
||
padding: 12rpx 16rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10rpx;
|
||
box-shadow: 0 8rpx 16rpx rgba(0, 0, 0, 0.06);
|
||
}
|
||
|
||
.notice-label {
|
||
font-size: 24rpx;
|
||
font-weight: 700;
|
||
color: #2fc6a7;
|
||
}
|
||
|
||
.notice-swiper {
|
||
height: 48rpx;
|
||
flex: 1;
|
||
}
|
||
|
||
.notice-item {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.notice-text {
|
||
font-size: 24rpx;
|
||
color: #111827;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.notice-more {
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
border-radius: 50%;
|
||
border: 1rpx solid #2fc6a7;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: #2fc6a7;
|
||
}
|
||
|
||
.notice-more-icon {
|
||
font-size: 22rpx;
|
||
line-height: 1;
|
||
}
|
||
|
||
.self-report-entry {
|
||
margin: 18rpx 24rpx 0;
|
||
display: flex;
|
||
gap: 14rpx;
|
||
}
|
||
|
||
.entry-btn {
|
||
flex: 1;
|
||
min-width: 0;
|
||
box-sizing: border-box;
|
||
padding: 22rpx 20rpx;
|
||
border-radius: 24rpx;
|
||
border: 1rpx solid #f6dcb4;
|
||
background: #fff5e2;
|
||
color: #e0631a;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 14rpx;
|
||
}
|
||
|
||
.entry-btn.primary {
|
||
background: linear-gradient(135deg, #f9863c, #f2620f);
|
||
border-color: transparent;
|
||
color: #ffffff;
|
||
box-shadow: 0 8rpx 20rpx rgba(242, 98, 15, 0.25);
|
||
}
|
||
|
||
.entry-icon {
|
||
flex-shrink: 0;
|
||
width: 72rpx;
|
||
height: 72rpx;
|
||
border-radius: 20rpx;
|
||
background: #ffffff;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.entry-text {
|
||
flex: 1;
|
||
min-width: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 6rpx;
|
||
}
|
||
|
||
.entry-title {
|
||
font-size: 32rpx;
|
||
font-weight: 700;
|
||
line-height: 1.2;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.entry-desc {
|
||
font-size: 20rpx;
|
||
line-height: 1.2;
|
||
opacity: 0.72;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.category-wrap {
|
||
padding: 4rpx 24rpx 18rpx;
|
||
//background: #f3fffb;
|
||
}
|
||
|
||
.category-chip {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
padding: 10rpx 20rpx;
|
||
margin-right: 16rpx;
|
||
border-radius: 999rpx;
|
||
font-size: 28rpx;
|
||
background: transparent;
|
||
color: #1f2937;
|
||
border: 1rpx solid transparent;
|
||
}
|
||
|
||
.category-chip.active {
|
||
background: #ff8a00;
|
||
color: #fff;
|
||
border-color: #ff8a00;
|
||
}
|
||
|
||
.filter-row {
|
||
margin: 0 24rpx 8rpx;
|
||
padding: 12rpx 0;
|
||
background: #f5f5f5;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
color: #5c6c84;
|
||
}
|
||
|
||
.location {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8rpx;
|
||
font-weight: 700;
|
||
color: #111827;
|
||
}
|
||
|
||
.sort-options {
|
||
display: flex;
|
||
gap: 12rpx;
|
||
font-size: 26rpx;
|
||
color: #2fc6a7;
|
||
}
|
||
|
||
.sort-item {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6rpx;
|
||
padding: 8rpx 14rpx;
|
||
border-radius: 999rpx;
|
||
border: 1rpx solid #2fc6a7;
|
||
color: #2fc6a7;
|
||
font-size: 22rpx;
|
||
}
|
||
|
||
.sort-item.active {
|
||
background: #2fc6a7;
|
||
color: #ffffff;
|
||
}
|
||
|
||
.sort-arrow {
|
||
font-size: 18rpx;
|
||
line-height: 1;
|
||
}
|
||
|
||
.task-list {
|
||
padding: 12rpx 24rpx 32rpx;
|
||
background: #f5f5f5;
|
||
}
|
||
|
||
.task-card {
|
||
background: #ffffff;
|
||
border-radius: 24rpx;
|
||
padding: 22rpx 20rpx;
|
||
margin-bottom: 18rpx;
|
||
border: 1rpx solid #eef2f7;
|
||
box-shadow: 0 8rpx 20rpx rgba(37, 99, 235, 0.06);
|
||
}
|
||
|
||
.task-head {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.date-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12rpx;
|
||
}
|
||
|
||
.date {
|
||
font-weight: 700;
|
||
color: #1f2937;
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
.relative {
|
||
padding: 6rpx 14rpx;
|
||
border-radius: 999rpx;
|
||
background: #fff7ed;
|
||
color: #f97316;
|
||
font-size: 22rpx;
|
||
border: 1rpx solid #fdba74;
|
||
}
|
||
|
||
.cate-tag {
|
||
padding: 6rpx 14rpx;
|
||
border-radius: 999rpx;
|
||
background: #eff6ff;
|
||
color: #2563eb;
|
||
font-size: 22rpx;
|
||
border: 1rpx solid #93c5fd;
|
||
}
|
||
|
||
.reward {
|
||
display: flex;
|
||
align-items: baseline;
|
||
font-weight: 800;
|
||
color: #ff3b30;
|
||
}
|
||
|
||
.currency {
|
||
font-size: 24rpx;
|
||
margin-right: 6rpx;
|
||
}
|
||
|
||
.reward-number {
|
||
font-size: 34rpx;
|
||
}
|
||
|
||
.task-title-row {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-top: 12rpx;
|
||
gap: 12rpx;
|
||
}
|
||
|
||
.task-title {
|
||
font-size: 30rpx;
|
||
font-weight: 700;
|
||
color: #0f172a;
|
||
flex: 1;
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.chain-pill {
|
||
max-width: 220rpx;
|
||
padding: 6rpx 14rpx;
|
||
border-radius: 999rpx;
|
||
background: #ecfdf5;
|
||
color: #059669;
|
||
border: 1rpx solid #a7f3d0;
|
||
font-size: 22rpx;
|
||
font-weight: 700;
|
||
line-height: 1.2;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.type-pill {
|
||
display: none;
|
||
}
|
||
|
||
.type-blue {
|
||
background: linear-gradient(135deg, #2563eb, #1d4ed8);
|
||
}
|
||
|
||
.type-purple {
|
||
background: linear-gradient(135deg, #7c3aed, #6d28d9);
|
||
}
|
||
|
||
.type-orange {
|
||
background: linear-gradient(135deg, #f97316, #ea580c);
|
||
}
|
||
|
||
.task-meta {
|
||
margin-top: 10rpx;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
color: #6b7280;
|
||
font-size: 24rpx;
|
||
gap: 12rpx;
|
||
}
|
||
|
||
.addr {
|
||
flex: 1;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.distance {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6rpx;
|
||
padding: 6rpx 10rpx;
|
||
border-radius: 12rpx;
|
||
color: #4b5563;
|
||
}
|
||
|
||
.tags {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 10rpx;
|
||
margin-top: 12rpx;
|
||
}
|
||
|
||
.tag {
|
||
padding: 8rpx 14rpx;
|
||
border-radius: 999rpx;
|
||
background: #f0f4ff;
|
||
color: var(--brand-primary);
|
||
font-size: 22rpx;
|
||
font-weight: 600;
|
||
border: 1rpx solid #d8e2ff;
|
||
}
|
||
|
||
.audit-box {
|
||
margin-top: 14rpx;
|
||
padding: 14rpx;
|
||
background: #f8fafc;
|
||
border-radius: 14rpx;
|
||
border: 1rpx solid #e5e7eb;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.audit-status {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
padding: 8rpx 14rpx;
|
||
border-radius: 999rpx;
|
||
font-size: 22rpx;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.audit-success {
|
||
background: #ecfdf3;
|
||
color: #15803d;
|
||
border: 1rpx solid #bbf7d0;
|
||
}
|
||
|
||
.audit-danger {
|
||
background: #fef2f2;
|
||
color: #dc2626;
|
||
border: 1rpx solid #fecdd3;
|
||
}
|
||
|
||
.audit-warn {
|
||
background: #fffbeb;
|
||
color: #d97706;
|
||
border: 1rpx solid #fde68a;
|
||
}
|
||
|
||
.audit-reason {
|
||
display: block;
|
||
margin-top: 8rpx;
|
||
color: #dc2626;
|
||
font-size: 22rpx;
|
||
}
|
||
|
||
.total-progress-card {
|
||
margin-top: 14rpx;
|
||
padding: 12rpx 14rpx;
|
||
background: #f8fafc;
|
||
border-radius: 14rpx;
|
||
border: 1rpx solid #e5e7eb;
|
||
}
|
||
|
||
.total-progress-head {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 10rpx;
|
||
}
|
||
|
||
.total-progress-title {
|
||
font-size: 26rpx;
|
||
font-weight: 700;
|
||
color: #0f172a;
|
||
}
|
||
|
||
.total-progress-value {
|
||
font-size: 22rpx;
|
||
color: #64748b;
|
||
}
|
||
|
||
.total-progress-bar {
|
||
height: 10rpx;
|
||
border-radius: 999rpx;
|
||
background: #e2e8f0;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.total-progress-fill {
|
||
height: 100%;
|
||
background: linear-gradient(90deg, #2fc6a7, #a1ded0);
|
||
}
|
||
|
||
.actions {
|
||
margin-top: 16rpx;
|
||
display: flex;
|
||
gap: 12rpx;
|
||
}
|
||
|
||
.primary-action,
|
||
.action-btn,
|
||
.info-action,
|
||
.ghost-action {
|
||
text-align: center;
|
||
padding: 18rpx 16rpx;
|
||
border-radius: 999rpx;
|
||
font-weight: 800;
|
||
font-size: 26rpx;
|
||
}
|
||
|
||
.primary-action {
|
||
flex: 2;
|
||
background: #2fc6a7;
|
||
color: #ffffff;
|
||
box-shadow: 0 8rpx 18rpx rgba(47, 198, 167, 0.25);
|
||
}
|
||
|
||
.ghost-action {
|
||
flex: 1;
|
||
background: #ffffff;
|
||
color: #2fc6a7;
|
||
border: 1rpx solid #2fc6a7;
|
||
}
|
||
|
||
.info-action {
|
||
width: 100rpx;
|
||
padding: 10rpx 20rpx;
|
||
font-size: 24rpx;
|
||
border: 1px solid rgb(75, 85, 99);
|
||
color: rgb(75, 85, 99);
|
||
}
|
||
|
||
.action-btn {
|
||
flex: 1;
|
||
background: linear-gradient(to right, #f97316, #ef4444);
|
||
color: #ffffff;
|
||
border: none;
|
||
box-shadow: 0 8rpx 18rpx #ffedd5;
|
||
}
|
||
|
||
.primary-action.full,
|
||
.ghost-action.full {
|
||
flex: 1;
|
||
}
|
||
|
||
.empty {
|
||
padding: 120rpx 0;
|
||
text-align: center;
|
||
color: #9ca3af;
|
||
}
|
||
|
||
.empty-icon {
|
||
font-size: 64rpx;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
.empty-text {
|
||
font-size: 26rpx;
|
||
}
|
||
|
||
.detail-popup {
|
||
padding: 36rpx 32rpx 28rpx;
|
||
max-height: 78vh;
|
||
overflow: auto;
|
||
}
|
||
|
||
.detail-popup-head {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 26rpx;
|
||
}
|
||
|
||
.detail-popup-title {
|
||
font-size: 38rpx;
|
||
font-weight: 800;
|
||
color: #0f172a;
|
||
}
|
||
|
||
.detail-popup-close {
|
||
width: 68rpx;
|
||
height: 68rpx;
|
||
border-radius: 50%;
|
||
background: #f1f5f9;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.detail-merchant-card {
|
||
margin-bottom: 22rpx;
|
||
background: #f8fafc;
|
||
border: 1rpx solid #e5e7eb;
|
||
border-radius: 20rpx;
|
||
padding: 18rpx 20rpx;
|
||
}
|
||
|
||
.detail-merchant-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 18rpx;
|
||
min-width: 0;
|
||
}
|
||
|
||
.detail-merchant-row + .detail-merchant-row {
|
||
margin-top: 14rpx;
|
||
}
|
||
|
||
.detail-merchant-label {
|
||
flex-shrink: 0;
|
||
width: 132rpx;
|
||
color: #64748b;
|
||
font-size: 24rpx;
|
||
}
|
||
|
||
.detail-merchant-value {
|
||
flex: 1;
|
||
min-width: 0;
|
||
color: #0f172a;
|
||
font-size: 28rpx;
|
||
font-weight: 700;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.detail-requirement {
|
||
background: #ecfdf5;
|
||
border: 1rpx solid #a7f3d0;
|
||
border-radius: 24rpx;
|
||
padding: 20rpx 22rpx;
|
||
}
|
||
|
||
.detail-block-head {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12rpx;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.nav-wrap {
|
||
background: url("https://yaodongxiao.oss-cn-guangzhou.aliyuncs.com/20260127/a0fceca8f9e439969e0e0a9a799b4db2.png");
|
||
background-size: cover;
|
||
background-position: bottom center;
|
||
border-radius: 20rpx;
|
||
padding: 10rpx;
|
||
//position: absolute;
|
||
//left: 20rpx;
|
||
//top: 635rpx;
|
||
//width: calc(100vw - 60rpx);
|
||
margin: 0 20rpx 20rpx;
|
||
}
|
||
|
||
.detail-icon {
|
||
width: 44rpx;
|
||
height: 44rpx;
|
||
border-radius: 12rpx;
|
||
//background: #d1fae5;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.detail-block-title {
|
||
font-size: 30rpx;
|
||
font-weight: 700;
|
||
color: #10b981;
|
||
}
|
||
|
||
.detail-block-text {
|
||
font-size: 26rpx;
|
||
color: #059669;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.detail-section {
|
||
margin-top: 26rpx;
|
||
}
|
||
|
||
.detail-section-title {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12rpx;
|
||
font-size: 30rpx;
|
||
font-weight: 700;
|
||
color: #1f2937;
|
||
}
|
||
|
||
.detail-section-dot {
|
||
width: 10rpx;
|
||
height: 32rpx;
|
||
border-radius: 999rpx;
|
||
}
|
||
|
||
.detail-section-dot.orange {
|
||
background: #f97316;
|
||
}
|
||
|
||
.detail-section-dot.green {
|
||
background: #10b981;
|
||
}
|
||
|
||
.detail-section-dot.blue {
|
||
background: #3b82f6;
|
||
}
|
||
|
||
.detail-section-card {
|
||
margin-top: 14rpx;
|
||
background: #f8fafc;
|
||
border-radius: 18rpx;
|
||
padding: 18rpx 20rpx;
|
||
color: #4b5563;
|
||
font-size: 26rpx;
|
||
border: 1rpx solid #edf2f7;
|
||
}
|
||
</style>
|