|
@@ -3,7 +3,7 @@
|
|
|
<u-navbar title="售卖时间" :autoBack="true" :placeholder="true"></u-navbar>
|
|
<u-navbar title="售卖时间" :autoBack="true" :placeholder="true"></u-navbar>
|
|
|
|
|
|
|
|
<view class="card">
|
|
<view class="card">
|
|
|
- <view class="label required">商品售卖日期(可多选)</view>
|
|
|
|
|
|
|
+ <view class="label required">菜品售卖日期</view>
|
|
|
<u-radio-group v-model="mode" @change="onModeChange">
|
|
<u-radio-group v-model="mode" @change="onModeChange">
|
|
|
<u-radio name="fixed" active-color="#449AFF">固定日期</u-radio>
|
|
<u-radio name="fixed" active-color="#449AFF">固定日期</u-radio>
|
|
|
<u-radio name="custom" active-color="#449AFF">自定义</u-radio>
|
|
<u-radio name="custom" active-color="#449AFF">自定义</u-radio>
|
|
@@ -11,12 +11,19 @@
|
|
|
</u-radio-group>
|
|
</u-radio-group>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
|
- <!-- 固定日期:简易月历多选 -->
|
|
|
|
|
|
|
+ <!-- 固定日期:月历多选 -->
|
|
|
<view class="card" v-if="mode === 'fixed'">
|
|
<view class="card" v-if="mode === 'fixed'">
|
|
|
<view class="cal-head">
|
|
<view class="cal-head">
|
|
|
- <u-icon name="arrow-left" @click="changeMonth(-1)"></u-icon>
|
|
|
|
|
- <text>{{ year }}年{{ month }}月</text>
|
|
|
|
|
- <u-icon name="arrow-right" @click="changeMonth(1)"></u-icon>
|
|
|
|
|
|
|
+ <view class="cal-month" @click="monthPickerShow = true">
|
|
|
|
|
+ <text>{{ year }}年{{ month }}月</text>
|
|
|
|
|
+ <u-icon name="arrow-down" color="#333" size="24"></u-icon>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="cal-all" @click="toggleSelectAll">
|
|
|
|
|
+ <text>全选</text>
|
|
|
|
|
+ <view :class="['check', isMonthAllSelected ? 'on' : '']">
|
|
|
|
|
+ <u-icon v-if="isMonthAllSelected" name="checkmark" color="#fff" size="18"></u-icon>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
</view>
|
|
</view>
|
|
|
<view class="week">
|
|
<view class="week">
|
|
|
<text v-for="w in weeks" :key="w">{{ w }}</text>
|
|
<text v-for="w in weeks" :key="w">{{ w }}</text>
|
|
@@ -28,24 +35,22 @@
|
|
|
:class="['day', !d ? 'empty' : '', d && selectedDates.includes(d.full) ? 'on' : '']"
|
|
:class="['day', !d ? 'empty' : '', d && selectedDates.includes(d.full) ? 'on' : '']"
|
|
|
@click="toggleDate(d)"
|
|
@click="toggleDate(d)"
|
|
|
>
|
|
>
|
|
|
- <template v-if="d">
|
|
|
|
|
- <text class="n">{{ d.day }}</text>
|
|
|
|
|
- </template>
|
|
|
|
|
|
|
+ <text v-if="d" class="n">{{ d.day }}</text>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
|
<!-- 自定义区间 -->
|
|
<!-- 自定义区间 -->
|
|
|
<view class="card" v-if="mode === 'custom'">
|
|
<view class="card" v-if="mode === 'custom'">
|
|
|
- <view class="row" @click="pickDate('start')">
|
|
|
|
|
- <text class="label required">开始日期</text>
|
|
|
|
|
- <text class="value">{{ startDate || '请选择开始日期' }}</text>
|
|
|
|
|
- <u-icon name="arrow-right" color="#C7C6CA"></u-icon>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="row" @click="pickDate('end')">
|
|
|
|
|
- <text class="label required">结束日期</text>
|
|
|
|
|
- <text class="value">{{ endDate || '请选择结束日期' }}</text>
|
|
|
|
|
- <u-icon name="arrow-right" color="#C7C6CA"></u-icon>
|
|
|
|
|
|
|
+ <view class="label">自定义时间</view>
|
|
|
|
|
+ <view class="range-row">
|
|
|
|
|
+ <view class="range-box" @click="openRangePicker('start')">
|
|
|
|
|
+ {{ startDate || '开始日期' }}
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <text class="range-sep">-</text>
|
|
|
|
|
+ <view class="range-box" @click="openRangePicker('end')">
|
|
|
|
|
+ {{ endDate || '结束日期' }}
|
|
|
|
|
+ </view>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
@@ -54,11 +59,63 @@
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
|
<view class="footer">
|
|
<view class="footer">
|
|
|
- <u-button shape="circle" plain type="primary" @click="goPrev">上一步</u-button>
|
|
|
|
|
- <u-button type="primary" shape="circle" :disabled="!canSubmit || submitting" @click="onSubmit">提交审核</u-button>
|
|
|
|
|
|
|
+ <view class="f-btn outline" @click="goPrev">上一步</view>
|
|
|
|
|
+ <view
|
|
|
|
|
+ :class="['f-btn', 'primary', !canSubmit || submitting ? 'disabled' : '']"
|
|
|
|
|
+ @click="onSubmit"
|
|
|
|
|
+ >
|
|
|
|
|
+ 提交审核
|
|
|
|
|
+ </view>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
|
- <u-calendar v-model="calendarShow" mode="date" max-date="2050-12-31" @change="onCalendar"></u-calendar>
|
|
|
|
|
|
|
+ <!-- 年月切换 -->
|
|
|
|
|
+ <view class="pick-mask" v-if="monthPickerShow" @click="monthPickerShow = false">
|
|
|
|
|
+ <view class="pick-sheet" @click.stop>
|
|
|
|
|
+ <view class="pick-title">选择月份</view>
|
|
|
|
|
+ <picker-view class="pick-view" :value="monthPickerValue" @change="onMonthPickerChange">
|
|
|
|
|
+ <picker-view-column>
|
|
|
|
|
+ <view class="pick-item" v-for="y in yearList" :key="y">{{ y }}年</view>
|
|
|
|
|
+ </picker-view-column>
|
|
|
|
|
+ <picker-view-column>
|
|
|
|
|
+ <view class="pick-item" v-for="m in 12" :key="m">{{ m }}月</view>
|
|
|
|
|
+ </picker-view-column>
|
|
|
|
|
+ </picker-view>
|
|
|
|
|
+ <view class="pick-confirm" @click="confirmMonthPicker">确定</view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 自定义:选择时间(起止) -->
|
|
|
|
|
+ <view class="pick-mask" v-if="rangePickerShow" @click="rangePickerShow = false">
|
|
|
|
|
+ <view class="pick-sheet" @click.stop>
|
|
|
|
|
+ <view class="pick-title">选择时间</view>
|
|
|
|
|
+ <view class="range-preview">
|
|
|
|
|
+ <view
|
|
|
|
|
+ :class="['preview-box', rangeField === 'start' ? 'on' : '']"
|
|
|
|
|
+ @click="switchRangeField('start')"
|
|
|
|
|
+ >
|
|
|
|
|
+ {{ formatCnDate(tempStart) || '开始日期' }}
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view
|
|
|
|
|
+ :class="['preview-box', rangeField === 'end' ? 'on' : '']"
|
|
|
|
|
+ @click="switchRangeField('end')"
|
|
|
|
|
+ >
|
|
|
|
|
+ {{ formatCnDate(tempEnd) || '结束日期' }}
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <picker-view class="pick-view" :value="datePickerValue" @change="onDatePickerChange">
|
|
|
|
|
+ <picker-view-column>
|
|
|
|
|
+ <view class="pick-item" v-for="y in yearList" :key="'dy' + y">{{ y }}年</view>
|
|
|
|
|
+ </picker-view-column>
|
|
|
|
|
+ <picker-view-column>
|
|
|
|
|
+ <view class="pick-item" v-for="m in 12" :key="'dm' + m">{{ m }}月</view>
|
|
|
|
|
+ </picker-view-column>
|
|
|
|
|
+ <picker-view-column>
|
|
|
|
|
+ <view class="pick-item" v-for="d in dayList" :key="'dd' + d">{{ d }}日</view>
|
|
|
|
|
+ </picker-view-column>
|
|
|
|
|
+ </picker-view>
|
|
|
|
|
+ <view class="pick-confirm" @click="confirmRangePicker">确定</view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
</view>
|
|
</view>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
@@ -74,18 +131,28 @@ import session from '../session.js';
|
|
|
export default {
|
|
export default {
|
|
|
data() {
|
|
data() {
|
|
|
const now = new Date();
|
|
const now = new Date();
|
|
|
|
|
+ const year = now.getFullYear();
|
|
|
|
|
+ const yearList = [];
|
|
|
|
|
+ for (let y = year - 2; y <= year + 10; y++) yearList.push(y);
|
|
|
return {
|
|
return {
|
|
|
fromMulti: false,
|
|
fromMulti: false,
|
|
|
mode: 'fixed',
|
|
mode: 'fixed',
|
|
|
- year: now.getFullYear(),
|
|
|
|
|
|
|
+ year,
|
|
|
month: now.getMonth() + 1,
|
|
month: now.getMonth() + 1,
|
|
|
weeks: ['日', '一', '二', '三', '四', '五', '六'],
|
|
weeks: ['日', '一', '二', '三', '四', '五', '六'],
|
|
|
selectedDates: [],
|
|
selectedDates: [],
|
|
|
startDate: '',
|
|
startDate: '',
|
|
|
endDate: '',
|
|
endDate: '',
|
|
|
- calendarShow: false,
|
|
|
|
|
- calendarField: 'start',
|
|
|
|
|
- submitting: false
|
|
|
|
|
|
|
+ submitting: false,
|
|
|
|
|
+ yearList,
|
|
|
|
|
+ monthPickerShow: false,
|
|
|
|
|
+ monthPickerValue: [2, now.getMonth()],
|
|
|
|
|
+ rangePickerShow: false,
|
|
|
|
|
+ rangeField: 'start',
|
|
|
|
|
+ tempStart: '',
|
|
|
|
|
+ tempEnd: '',
|
|
|
|
|
+ datePickerValue: [2, now.getMonth(), now.getDate() - 1],
|
|
|
|
|
+ pickerDay: now.getDate()
|
|
|
};
|
|
};
|
|
|
},
|
|
},
|
|
|
computed: {
|
|
computed: {
|
|
@@ -102,6 +169,24 @@ export default {
|
|
|
}
|
|
}
|
|
|
return arr;
|
|
return arr;
|
|
|
},
|
|
},
|
|
|
|
|
+ monthDateList() {
|
|
|
|
|
+ return this.days.filter(Boolean).map((d) => d.full);
|
|
|
|
|
+ },
|
|
|
|
|
+ isMonthAllSelected() {
|
|
|
|
|
+ const list = this.monthDateList;
|
|
|
|
|
+ if (!list.length) return false;
|
|
|
|
|
+ return list.every((d) => this.selectedDates.includes(d));
|
|
|
|
|
+ },
|
|
|
|
|
+ dayList() {
|
|
|
|
|
+ const y =
|
|
|
|
|
+ this.yearList[this.datePickerValue[0]] ||
|
|
|
|
|
+ this.year;
|
|
|
|
|
+ const m = (this.datePickerValue[1] || 0) + 1;
|
|
|
|
|
+ const total = new Date(y, m, 0).getDate();
|
|
|
|
|
+ const arr = [];
|
|
|
|
|
+ for (let d = 1; d <= total; d++) arr.push(d);
|
|
|
|
|
+ return arr;
|
|
|
|
|
+ },
|
|
|
canSubmit() {
|
|
canSubmit() {
|
|
|
if (this.mode === 'all') return true;
|
|
if (this.mode === 'all') return true;
|
|
|
if (this.mode === 'fixed') return this.selectedDates.length > 0;
|
|
if (this.mode === 'fixed') return this.selectedDates.length > 0;
|
|
@@ -111,24 +196,32 @@ export default {
|
|
|
onLoad(query) {
|
|
onLoad(query) {
|
|
|
this.fromMulti = query.from === 'multi';
|
|
this.fromMulti = query.from === 'multi';
|
|
|
if (this.fromMulti) this.mode = 'fixed';
|
|
if (this.fromMulti) this.mode = 'fixed';
|
|
|
|
|
+ this.syncMonthPickerValue();
|
|
|
this.hydrateFromEditExtra();
|
|
this.hydrateFromEditExtra();
|
|
|
},
|
|
},
|
|
|
methods: {
|
|
methods: {
|
|
|
- /** 将日期统一为 YYYY-MM-DD,兼容接口带时分秒 */
|
|
|
|
|
normalizeDate(val) {
|
|
normalizeDate(val) {
|
|
|
if (!val) return '';
|
|
if (!val) return '';
|
|
|
const s = String(val).trim();
|
|
const s = String(val).trim();
|
|
|
const m = s.match(/^(\d{4}-\d{2}-\d{2})/);
|
|
const m = s.match(/^(\d{4}-\d{2}-\d{2})/);
|
|
|
return m ? m[1] : s.slice(0, 10);
|
|
return m ? m[1] : s.slice(0, 10);
|
|
|
},
|
|
},
|
|
|
- /** 编辑回显:优先根据 saleTimes 内容推断,不依赖顶层 saleDateType 一定有值 */
|
|
|
|
|
|
|
+ formatCnDate(val) {
|
|
|
|
|
+ const d = this.normalizeDate(val);
|
|
|
|
|
+ if (!d) return '';
|
|
|
|
|
+ const [y, m, day] = d.split('-');
|
|
|
|
|
+ return `${Number(y)}年${Number(m)}月${Number(day)}日`;
|
|
|
|
|
+ },
|
|
|
|
|
+ toYmd(y, m, d) {
|
|
|
|
|
+ return `${y}-${String(m).padStart(2, '0')}-${String(d).padStart(2, '0')}`;
|
|
|
|
|
+ },
|
|
|
|
|
+ /** 编辑回显:优先根据 saleTimes 内容推断 */
|
|
|
hydrateFromEditExtra() {
|
|
hydrateFromEditExtra() {
|
|
|
const extra = session.getDishEditExtra() || {};
|
|
const extra = session.getDishEditExtra() || {};
|
|
|
const saleTimes = Array.isArray(extra.saleTimes) ? extra.saleTimes : [];
|
|
const saleTimes = Array.isArray(extra.saleTimes) ? extra.saleTimes : [];
|
|
|
const saleDateType = Number(extra.saleDateType);
|
|
const saleDateType = Number(extra.saleDateType);
|
|
|
if (!saleTimes.length && !saleDateType) return;
|
|
if (!saleTimes.length && !saleDateType) return;
|
|
|
|
|
|
|
|
- // 自定义区间:saleType=2 或带起止日期(接口可能不回顶层 saleDateType)
|
|
|
|
|
const range = saleTimes.find(
|
|
const range = saleTimes.find(
|
|
|
(t) => Number(t.saleType) === 2 || t.startDate || t.endDate
|
|
(t) => Number(t.saleType) === 2 || t.startDate || t.endDate
|
|
|
);
|
|
);
|
|
@@ -139,7 +232,6 @@ export default {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 固定日期:saleType=1 或带 saleDate
|
|
|
|
|
const fixedDates = saleTimes
|
|
const fixedDates = saleTimes
|
|
|
.filter((t) => Number(t.saleType) === 1 || t.saleDate)
|
|
.filter((t) => Number(t.saleType) === 1 || t.saleDate)
|
|
|
.map((t) => this.normalizeDate(t.saleDate))
|
|
.map((t) => this.normalizeDate(t.saleDate))
|
|
@@ -152,50 +244,129 @@ export default {
|
|
|
const [y, m] = fixedDates[0].split('-').map(Number);
|
|
const [y, m] = fixedDates[0].split('-').map(Number);
|
|
|
if (y) this.year = y;
|
|
if (y) this.year = y;
|
|
|
if (m) this.month = m;
|
|
if (m) this.month = m;
|
|
|
|
|
+ this.syncMonthPickerValue();
|
|
|
}
|
|
}
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 无具体区间:全部日期售卖(仅多规格入口有该选项)
|
|
|
|
|
if ((saleDateType === 2 || !saleTimes.length) && this.fromMulti) {
|
|
if ((saleDateType === 2 || !saleTimes.length) && this.fromMulti) {
|
|
|
this.mode = 'all';
|
|
this.mode = 'all';
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
|
|
+ syncMonthPickerValue() {
|
|
|
|
|
+ const yi = this.yearList.indexOf(this.year);
|
|
|
|
|
+ this.monthPickerValue = [yi >= 0 ? yi : 0, this.month - 1];
|
|
|
|
|
+ },
|
|
|
onModeChange() {
|
|
onModeChange() {
|
|
|
this.selectedDates = [];
|
|
this.selectedDates = [];
|
|
|
this.startDate = '';
|
|
this.startDate = '';
|
|
|
this.endDate = '';
|
|
this.endDate = '';
|
|
|
},
|
|
},
|
|
|
- changeMonth(delta) {
|
|
|
|
|
- let m = this.month + delta;
|
|
|
|
|
- let y = this.year;
|
|
|
|
|
- if (m < 1) {
|
|
|
|
|
- m = 12;
|
|
|
|
|
- y -= 1;
|
|
|
|
|
- }
|
|
|
|
|
- if (m > 12) {
|
|
|
|
|
- m = 1;
|
|
|
|
|
- y += 1;
|
|
|
|
|
- }
|
|
|
|
|
- this.month = m;
|
|
|
|
|
- this.year = y;
|
|
|
|
|
- },
|
|
|
|
|
toggleDate(d) {
|
|
toggleDate(d) {
|
|
|
if (!d) return;
|
|
if (!d) return;
|
|
|
const i = this.selectedDates.indexOf(d.full);
|
|
const i = this.selectedDates.indexOf(d.full);
|
|
|
if (i >= 0) this.selectedDates.splice(i, 1);
|
|
if (i >= 0) this.selectedDates.splice(i, 1);
|
|
|
else this.selectedDates.push(d.full);
|
|
else this.selectedDates.push(d.full);
|
|
|
},
|
|
},
|
|
|
- pickDate(field) {
|
|
|
|
|
- this.calendarField = field;
|
|
|
|
|
- this.calendarShow = true;
|
|
|
|
|
|
|
+ toggleSelectAll() {
|
|
|
|
|
+ const list = this.monthDateList;
|
|
|
|
|
+ if (this.isMonthAllSelected) {
|
|
|
|
|
+ this.selectedDates = this.selectedDates.filter((d) => !list.includes(d));
|
|
|
|
|
+ } else {
|
|
|
|
|
+ const set = new Set(this.selectedDates.concat(list));
|
|
|
|
|
+ this.selectedDates = Array.from(set).sort();
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ onMonthPickerChange(e) {
|
|
|
|
|
+ this.monthPickerValue = e.detail.value || [0, 0];
|
|
|
|
|
+ },
|
|
|
|
|
+ confirmMonthPicker() {
|
|
|
|
|
+ const [yi, mi] = this.monthPickerValue;
|
|
|
|
|
+ this.year = this.yearList[yi] || this.year;
|
|
|
|
|
+ this.month = (mi || 0) + 1;
|
|
|
|
|
+ this.monthPickerShow = false;
|
|
|
|
|
+ },
|
|
|
|
|
+ openRangePicker(field) {
|
|
|
|
|
+ this.rangeField = field || 'start';
|
|
|
|
|
+ this.tempStart = this.startDate;
|
|
|
|
|
+ this.tempEnd = this.endDate;
|
|
|
|
|
+ const base = this.normalizeDate(
|
|
|
|
|
+ field === 'end' ? this.endDate || this.startDate : this.startDate || this.endDate
|
|
|
|
|
+ );
|
|
|
|
|
+ this.applyDateToPicker(base);
|
|
|
|
|
+ // 首次打开且对应字段为空时,用当前滚轮日期预填
|
|
|
|
|
+ const now = new Date();
|
|
|
|
|
+ const fallback = this.toYmd(now.getFullYear(), now.getMonth() + 1, now.getDate());
|
|
|
|
|
+ const seed = base || fallback;
|
|
|
|
|
+ if (field === 'start' && !this.tempStart) this.tempStart = seed;
|
|
|
|
|
+ if (field === 'end' && !this.tempEnd) this.tempEnd = seed;
|
|
|
|
|
+ this.rangePickerShow = true;
|
|
|
|
|
+ },
|
|
|
|
|
+ switchRangeField(field) {
|
|
|
|
|
+ this.rangeField = field;
|
|
|
|
|
+ const base = this.normalizeDate(field === 'start' ? this.tempStart : this.tempEnd);
|
|
|
|
|
+ this.applyDateToPicker(base);
|
|
|
|
|
+ },
|
|
|
|
|
+ applyDateToPicker(ymd) {
|
|
|
|
|
+ const now = new Date();
|
|
|
|
|
+ let y = now.getFullYear();
|
|
|
|
|
+ let m = now.getMonth() + 1;
|
|
|
|
|
+ let d = now.getDate();
|
|
|
|
|
+ if (ymd) {
|
|
|
|
|
+ const parts = ymd.split('-').map(Number);
|
|
|
|
|
+ y = parts[0] || y;
|
|
|
|
|
+ m = parts[1] || m;
|
|
|
|
|
+ d = parts[2] || d;
|
|
|
|
|
+ }
|
|
|
|
|
+ let yi = this.yearList.indexOf(y);
|
|
|
|
|
+ if (yi < 0) yi = 0;
|
|
|
|
|
+ const total = new Date(y, m, 0).getDate();
|
|
|
|
|
+ if (d > total) d = total;
|
|
|
|
|
+ this.pickerDay = d;
|
|
|
|
|
+ this.datePickerValue = [yi, m - 1, d - 1];
|
|
|
|
|
+ this.$nextTick(() => {
|
|
|
|
|
+ this.datePickerValue = [yi, m - 1, d - 1];
|
|
|
|
|
+ });
|
|
|
|
|
+ },
|
|
|
|
|
+ onDatePickerChange(e) {
|
|
|
|
|
+ const val = e.detail.value || [0, 0, 0];
|
|
|
|
|
+ const y = this.yearList[val[0]] || this.year;
|
|
|
|
|
+ const m = (val[1] || 0) + 1;
|
|
|
|
|
+ const total = new Date(y, m, 0).getDate();
|
|
|
|
|
+ let d = (val[2] || 0) + 1;
|
|
|
|
|
+ if (d > total) d = total;
|
|
|
|
|
+ this.pickerDay = d;
|
|
|
|
|
+ this.datePickerValue = [val[0], val[1], d - 1];
|
|
|
|
|
+ const ymd = this.toYmd(y, m, d);
|
|
|
|
|
+ if (this.rangeField === 'start') this.tempStart = ymd;
|
|
|
|
|
+ else this.tempEnd = ymd;
|
|
|
},
|
|
},
|
|
|
- onCalendar(e) {
|
|
|
|
|
- const val = (e && e.result) || e;
|
|
|
|
|
- if (!val || typeof val !== 'string') return;
|
|
|
|
|
- if (this.calendarField === 'start') this.startDate = val;
|
|
|
|
|
- else this.endDate = val;
|
|
|
|
|
- this.calendarShow = false;
|
|
|
|
|
|
|
+ confirmRangePicker() {
|
|
|
|
|
+ // 滚轮停住后若未触发 change,用当前索引兜底写入当前字段
|
|
|
|
|
+ const [yi, mi, di] = this.datePickerValue;
|
|
|
|
|
+ const y = this.yearList[yi] || this.year;
|
|
|
|
|
+ const m = (mi || 0) + 1;
|
|
|
|
|
+ const total = new Date(y, m, 0).getDate();
|
|
|
|
|
+ let d = (di || 0) + 1;
|
|
|
|
|
+ if (d > total) d = total;
|
|
|
|
|
+ const ymd = this.toYmd(y, m, d);
|
|
|
|
|
+ if (this.rangeField === 'start') {
|
|
|
|
|
+ if (!this.tempStart) this.tempStart = ymd;
|
|
|
|
|
+ } else if (!this.tempEnd) {
|
|
|
|
|
+ this.tempEnd = ymd;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!this.tempStart || !this.tempEnd) {
|
|
|
|
|
+ uni.showToast({ title: '请选择开始和结束日期', icon: 'none' });
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (this.tempStart > this.tempEnd) {
|
|
|
|
|
+ uni.showToast({ title: '开始日期不能晚于结束日期', icon: 'none' });
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ this.startDate = this.tempStart;
|
|
|
|
|
+ this.endDate = this.tempEnd;
|
|
|
|
|
+ this.rangePickerShow = false;
|
|
|
},
|
|
},
|
|
|
goPrev() {
|
|
goPrev() {
|
|
|
uni.navigateBack();
|
|
uni.navigateBack();
|
|
@@ -204,11 +375,14 @@ export default {
|
|
|
if (this.mode === 'fixed') {
|
|
if (this.mode === 'fixed') {
|
|
|
return {
|
|
return {
|
|
|
saleDateType: 1,
|
|
saleDateType: 1,
|
|
|
- saleTimes: this.selectedDates.map((d, i) => ({
|
|
|
|
|
- saleType: 1,
|
|
|
|
|
- saleDate: d,
|
|
|
|
|
- sort: i
|
|
|
|
|
- }))
|
|
|
|
|
|
|
+ saleTimes: this.selectedDates
|
|
|
|
|
+ .slice()
|
|
|
|
|
+ .sort()
|
|
|
|
|
+ .map((d, i) => ({
|
|
|
|
|
+ saleType: 1,
|
|
|
|
|
+ saleDate: d,
|
|
|
|
|
+ sort: i
|
|
|
|
|
+ }))
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
|
if (this.mode === 'custom') {
|
|
if (this.mode === 'custom') {
|
|
@@ -224,7 +398,6 @@ export default {
|
|
|
]
|
|
]
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
|
- // 全部日期:不传具体日期区间,交由后端默认处理
|
|
|
|
|
return {
|
|
return {
|
|
|
saleDateType: 2,
|
|
saleDateType: 2,
|
|
|
saleTimes: []
|
|
saleTimes: []
|
|
@@ -249,7 +422,6 @@ export default {
|
|
|
buildSpecsAndSkus(draft) {
|
|
buildSpecsAndSkus(draft) {
|
|
|
const specs = [];
|
|
const specs = [];
|
|
|
(draft.specs || []).forEach((spec, si) => {
|
|
(draft.specs || []).forEach((spec, si) => {
|
|
|
- // 选中/未选中都提交,isSelected: 1 选中,0 未选中
|
|
|
|
|
(spec.values || []).forEach((v, oi) => {
|
|
(spec.values || []).forEach((v, oi) => {
|
|
|
const row = {
|
|
const row = {
|
|
|
specName: spec.name,
|
|
specName: spec.name,
|
|
@@ -258,14 +430,12 @@ export default {
|
|
|
optionSort: oi,
|
|
optionSort: oi,
|
|
|
isSelected: v.selected ? 1 : 0
|
|
isSelected: v.selected ? 1 : 0
|
|
|
};
|
|
};
|
|
|
- // 编辑时回传后端规格选项 id
|
|
|
|
|
if (v.persistId) row.id = v.persistId;
|
|
if (v.persistId) row.id = v.persistId;
|
|
|
specs.push(row);
|
|
specs.push(row);
|
|
|
});
|
|
});
|
|
|
});
|
|
});
|
|
|
const skus = (draft.combos || []).map((c, i) => ({
|
|
const skus = (draft.combos || []).map((c, i) => ({
|
|
|
specOptionNames: (c.label || '').replace(/\s*\|\s*/g, '/'),
|
|
specOptionNames: (c.label || '').replace(/\s*\|\s*/g, '/'),
|
|
|
- // 售价 price,原价 sellingPrice
|
|
|
|
|
price: Number(c.price || 0),
|
|
price: Number(c.price || 0),
|
|
|
sellingPrice: Number(c.sellingPrice != null ? c.sellingPrice : c.originPrice || 0),
|
|
sellingPrice: Number(c.sellingPrice != null ? c.sellingPrice : c.originPrice || 0),
|
|
|
stock: Number(c.stock || 0),
|
|
stock: Number(c.stock || 0),
|
|
@@ -297,7 +467,6 @@ export default {
|
|
|
payload.id = draft.editId;
|
|
payload.id = draft.editId;
|
|
|
}
|
|
}
|
|
|
if (!isMulti) {
|
|
if (!isMulti) {
|
|
|
- // 售价 price,原价 sellingPrice
|
|
|
|
|
payload.price = Number(draft.price || 0);
|
|
payload.price = Number(draft.price || 0);
|
|
|
payload.sellingPrice = Number(draft.sellingPrice || 0);
|
|
payload.sellingPrice = Number(draft.sellingPrice || 0);
|
|
|
payload.dailyStock = Number(draft.stock || 0);
|
|
payload.dailyStock = Number(draft.stock || 0);
|
|
@@ -308,7 +477,6 @@ export default {
|
|
|
const { specs, skus } = this.buildSpecsAndSkus(draft);
|
|
const { specs, skus } = this.buildSpecsAndSkus(draft);
|
|
|
payload.specs = specs;
|
|
payload.specs = specs;
|
|
|
payload.skus = skus;
|
|
payload.skus = skus;
|
|
|
- // 多规格时取第一档作为展示价/库存兜底
|
|
|
|
|
const first = (draft.combos || [])[0] || {};
|
|
const first = (draft.combos || [])[0] || {};
|
|
|
payload.price = Number(first.price || 0);
|
|
payload.price = Number(first.price || 0);
|
|
|
payload.sellingPrice = Number(
|
|
payload.sellingPrice = Number(
|
|
@@ -379,6 +547,7 @@ export default {
|
|
|
&.required::before {
|
|
&.required::before {
|
|
|
content: '*';
|
|
content: '*';
|
|
|
color: #ff4d4f;
|
|
color: #ff4d4f;
|
|
|
|
|
+ margin-right: 4rpx;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -386,12 +555,40 @@ export default {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
justify-content: space-between;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
|
|
+ margin-bottom: 24rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.cal-month {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 8rpx;
|
|
|
font-size: 30rpx;
|
|
font-size: 30rpx;
|
|
|
font-weight: 600;
|
|
font-weight: 600;
|
|
|
- margin-bottom: 20rpx;
|
|
|
|
|
|
|
+ color: #222;
|
|
|
|
|
+}
|
|
|
|
|
+.cal-all {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 12rpx;
|
|
|
|
|
+ font-size: 26rpx;
|
|
|
|
|
+ color: #666;
|
|
|
|
|
+ .check {
|
|
|
|
|
+ width: 32rpx;
|
|
|
|
|
+ height: 32rpx;
|
|
|
|
|
+ border-radius: 6rpx;
|
|
|
|
|
+ border: 2rpx solid #c7c6ca;
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ &.on {
|
|
|
|
|
+ background: #449aff;
|
|
|
|
|
+ border-color: #449aff;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
.week {
|
|
.week {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
|
|
+ margin-bottom: 8rpx;
|
|
|
text {
|
|
text {
|
|
|
width: 14.28%;
|
|
width: 14.28%;
|
|
|
text-align: center;
|
|
text-align: center;
|
|
@@ -404,56 +601,153 @@ export default {
|
|
|
flex-wrap: wrap;
|
|
flex-wrap: wrap;
|
|
|
.day {
|
|
.day {
|
|
|
width: 14.28%;
|
|
width: 14.28%;
|
|
|
- height: 80rpx;
|
|
|
|
|
|
|
+ height: 88rpx;
|
|
|
display: flex;
|
|
display: flex;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
justify-content: center;
|
|
|
.n {
|
|
.n {
|
|
|
- width: 56rpx;
|
|
|
|
|
- height: 56rpx;
|
|
|
|
|
- line-height: 56rpx;
|
|
|
|
|
|
|
+ width: 72rpx;
|
|
|
|
|
+ height: 72rpx;
|
|
|
|
|
+ line-height: 70rpx;
|
|
|
text-align: center;
|
|
text-align: center;
|
|
|
- border-radius: 50%;
|
|
|
|
|
- font-size: 26rpx;
|
|
|
|
|
|
|
+ border-radius: 12rpx;
|
|
|
|
|
+ font-size: 28rpx;
|
|
|
|
|
+ color: #333;
|
|
|
|
|
+ border: 2rpx solid transparent;
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
}
|
|
}
|
|
|
&.on .n {
|
|
&.on .n {
|
|
|
- background: #449aff;
|
|
|
|
|
- color: #fff;
|
|
|
|
|
|
|
+ background: rgba(68, 154, 255, 0.12);
|
|
|
|
|
+ border-color: #449aff;
|
|
|
|
|
+ color: #449aff;
|
|
|
}
|
|
}
|
|
|
&.empty {
|
|
&.empty {
|
|
|
pointer-events: none;
|
|
pointer-events: none;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-.row {
|
|
|
|
|
|
|
+.range-row {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
- min-height: 96rpx;
|
|
|
|
|
- border-bottom: 1rpx solid #f0f0f0;
|
|
|
|
|
- .label {
|
|
|
|
|
- width: 180rpx;
|
|
|
|
|
- margin-bottom: 0;
|
|
|
|
|
|
|
+ gap: 16rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.range-box {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ height: 80rpx;
|
|
|
|
|
+ line-height: 80rpx;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ font-size: 28rpx;
|
|
|
|
|
+ color: #333;
|
|
|
|
|
+ background: #f7f8fc;
|
|
|
|
|
+ border-radius: 12rpx;
|
|
|
|
|
+ border: 1rpx solid #e8e8e8;
|
|
|
|
|
+}
|
|
|
|
|
+.range-sep {
|
|
|
|
|
+ color: #999;
|
|
|
|
|
+ font-size: 28rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.footer {
|
|
|
|
|
+ position: fixed;
|
|
|
|
|
+ left: 0;
|
|
|
|
|
+ right: 0;
|
|
|
|
|
+ bottom: 0;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ gap: 24rpx;
|
|
|
|
|
+ padding: 20rpx 40rpx calc(20rpx + env(safe-area-inset-bottom));
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+}
|
|
|
|
|
+.f-btn {
|
|
|
|
|
+ height: 88rpx;
|
|
|
|
|
+ line-height: 88rpx;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ font-size: 30rpx;
|
|
|
|
|
+ border-radius: 44rpx;
|
|
|
|
|
+ &.outline {
|
|
|
|
|
+ width: 240rpx;
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+ color: #449aff;
|
|
|
|
|
+ border: 2rpx solid #449aff;
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
|
+ line-height: 84rpx;
|
|
|
|
|
+ background: #fff;
|
|
|
}
|
|
}
|
|
|
- .value {
|
|
|
|
|
|
|
+ &.primary {
|
|
|
flex: 1;
|
|
flex: 1;
|
|
|
- text-align: right;
|
|
|
|
|
- color: #666;
|
|
|
|
|
- font-size: 28rpx;
|
|
|
|
|
|
|
+ color: #fff;
|
|
|
|
|
+ background: linear-gradient(90deg, #77b6ff 0%, #449aff 100%);
|
|
|
|
|
+ }
|
|
|
|
|
+ &.disabled {
|
|
|
|
|
+ opacity: 0.5;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-.footer {
|
|
|
|
|
|
|
+.pick-mask {
|
|
|
position: fixed;
|
|
position: fixed;
|
|
|
left: 0;
|
|
left: 0;
|
|
|
right: 0;
|
|
right: 0;
|
|
|
|
|
+ top: 0;
|
|
|
bottom: 0;
|
|
bottom: 0;
|
|
|
|
|
+ background: rgba(0, 0, 0, 0.45);
|
|
|
|
|
+ z-index: 1000;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: flex-end;
|
|
|
|
|
+}
|
|
|
|
|
+.pick-sheet {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+ border-radius: 24rpx 24rpx 0 0;
|
|
|
|
|
+ padding: 0 32rpx calc(24rpx + env(safe-area-inset-bottom));
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
|
+}
|
|
|
|
|
+.pick-title {
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ font-size: 32rpx;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: #222;
|
|
|
|
|
+ padding: 36rpx 0 24rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.range-preview {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
gap: 20rpx;
|
|
gap: 20rpx;
|
|
|
- padding: 20rpx 40rpx calc(20rpx + env(safe-area-inset-bottom));
|
|
|
|
|
|
|
+ margin-bottom: 12rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.preview-box {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ height: 72rpx;
|
|
|
|
|
+ line-height: 70rpx;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ font-size: 26rpx;
|
|
|
|
|
+ color: #666;
|
|
|
background: #fff;
|
|
background: #fff;
|
|
|
- ::v-deep .u-btn {
|
|
|
|
|
- flex: 1;
|
|
|
|
|
|
|
+ border: 2rpx solid #e5e5e5;
|
|
|
|
|
+ border-radius: 12rpx;
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
|
+ &.on {
|
|
|
|
|
+ color: #449aff;
|
|
|
|
|
+ border-color: #449aff;
|
|
|
|
|
+ background: rgba(68, 154, 255, 0.06);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+.pick-view {
|
|
|
|
|
+ height: 400rpx;
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+}
|
|
|
|
|
+.pick-item {
|
|
|
|
|
+ height: 80rpx;
|
|
|
|
|
+ line-height: 80rpx;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ font-size: 30rpx;
|
|
|
|
|
+ color: #333;
|
|
|
|
|
+}
|
|
|
|
|
+.pick-confirm {
|
|
|
|
|
+ margin-top: 12rpx;
|
|
|
|
|
+ height: 88rpx;
|
|
|
|
|
+ line-height: 88rpx;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ font-size: 30rpx;
|
|
|
|
|
+ color: #fff;
|
|
|
|
|
+ border-radius: 44rpx;
|
|
|
|
|
+ background: linear-gradient(90deg, #77b6ff 0%, #449aff 100%);
|
|
|
|
|
+}
|
|
|
::v-deep .u-radio {
|
|
::v-deep .u-radio {
|
|
|
margin-right: 24rpx;
|
|
margin-right: 24rpx;
|
|
|
margin-bottom: 12rpx;
|
|
margin-bottom: 12rpx;
|