Browse Source

修改 bug

master
王泽彦 5 months ago
parent
commit
cdc449472b
  1. 2
      niucloud/app/api/controller/student/StudentController.php
  2. 113
      uniapp/components/order-form-popup/index.vue
  3. 66
      uniapp/components/schedule/ScheduleDetail.vue
  4. 95
      uniapp/pages-market/clue/clue_info.vue
  5. 2123
      uniapp/pages/market/clue/add_clues.vue
  6. 623
      uniapp/pages/market/clue/class_arrangement.vue
  7. 1292
      uniapp/pages/market/clue/class_arrangement_detail.vue
  8. 431
      uniapp/pages/market/clue/clue_info.less
  9. 1567
      uniapp/pages/market/clue/clue_info.vue
  10. 164
      uniapp/pages/market/clue/clue_table.vue
  11. 1806
      uniapp/pages/market/clue/edit_clues.vue
  12. 365
      uniapp/pages/market/clue/edit_clues_log.vue
  13. 1749
      uniapp/pages/market/clue/index.vue
  14. 343
      uniapp/pages/market/data/statistics.vue
  15. 427
      uniapp/pages/market/index/index.vue
  16. 495
      uniapp/pages/market/my/campus_data.vue
  17. 445
      uniapp/pages/market/my/dept_data.vue
  18. 523
      uniapp/pages/market/my/index.vue
  19. 505
      uniapp/pages/market/my/info.vue
  20. 292
      uniapp/pages/market/my/my_data.vue
  21. 105
      uniapp/pages/market/my/set_up.vue
  22. 275
      uniapp/pages/market/my/signed_client_list.vue
  23. 225
      uniapp/pages/market/my/update_pass.vue
  24. 272
      uniapp/pages/market/reimbursement/add.vue
  25. 136
      uniapp/pages/market/reimbursement/detail.vue
  26. 144
      uniapp/pages/market/reimbursement/list.vue

2
niucloud/app/api/controller/student/StudentController.php

@ -374,4 +374,6 @@ class StudentController extends BaseController
} }
return success($res['data']); return success($res['data']);
} }
} }

113
uniapp/components/order-form-popup/index.vue

@ -77,9 +77,19 @@
</view> </view>
</view> </view>
<view class="form-item">
<text class="label">班级选择</text>
<view class="picker-wrapper" @click="showClassPicker">
<text class="picker-text" :class="{ 'placeholder': !formData.class_id }">
{{ selectedClass && selectedClass.label || '请选择班级(可选)' }}
</text>
<text class="picker-arrow"></text>
</view>
</view>
<view class="form-item"> <view class="form-item">
<text class="label">备注</text> <text class="label">备注</text>
<textarea <textarea
class="form-textarea" class="form-textarea"
v-model="formData.remark" v-model="formData.remark"
placeholder="请输入备注信息" placeholder="请输入备注信息"
@ -147,7 +157,7 @@ export default {
gift_id: '', // ID gift_id: '', // ID
gift_type: '', // 1-, 2- gift_type: '', // 1-, 2-
remark: '', remark: '',
class_id: '1' // ID class_id: '' // ID
}, },
// //
@ -163,12 +173,14 @@ export default {
orderTypeSelectedIndex: -1, orderTypeSelectedIndex: -1,
giftSelectedIndex: -1, giftSelectedIndex: -1,
giftTypeSelectedIndex: -1, giftTypeSelectedIndex: -1,
classSelectedIndex: -1,
// //
courseList: [], courseList: [],
paymentTypes: [], // paymentTypes: [], //
orderTypes: [], // orderTypes: [], //
giftList: [], // giftList: [], //
classList: [], //
giftTypes: [ // giftTypes: [ //
{ value: '1', label: '减现' }, { value: '1', label: '减现' },
{ value: '2', label: '赠课' } { value: '2', label: '赠课' }
@ -190,6 +202,9 @@ export default {
}, },
selectedGiftType() { selectedGiftType() {
return this.giftTypes.find(item => item.value === this.formData.gift_type) return this.giftTypes.find(item => item.value === this.formData.gift_type)
},
selectedClass() {
return this.classList.find(item => item.value == this.formData.class_id)
} }
}, },
watch: { watch: {
@ -201,12 +216,17 @@ export default {
this.loadCourseList() this.loadCourseList()
this.loadDictionaries() // this.loadDictionaries() //
this.loadGiftList() // this.loadGiftList() //
this.loadClassList() //
} }
}, },
studentInfo: { studentInfo: {
handler(newVal) { handler(newVal) {
if (newVal && newVal.id) { if (newVal && newVal.id) {
this.formData.student_id = newVal.id this.formData.student_id = newVal.id
//
if (newVal.campus_id) {
this.loadClassList()
}
} }
}, },
immediate: true, immediate: true,
@ -221,6 +241,7 @@ export default {
this.loadCourseList() this.loadCourseList()
this.loadDictionaries() this.loadDictionaries()
this.loadGiftList() this.loadGiftList()
this.loadClassList()
} }
}, },
methods: { methods: {
@ -236,7 +257,7 @@ export default {
gift_id: '', gift_id: '',
gift_type: '', gift_type: '',
remark: '', remark: '',
class_id: '1' // ID class_id: '' // ID
} }
// //
@ -245,6 +266,7 @@ export default {
this.orderTypeSelectedIndex = -1 this.orderTypeSelectedIndex = -1
this.giftSelectedIndex = -1 this.giftSelectedIndex = -1
this.giftTypeSelectedIndex = -1 this.giftTypeSelectedIndex = -1
this.classSelectedIndex = -1
}, },
async loadCourseList() { async loadCourseList() {
@ -300,7 +322,9 @@ export default {
{ value: 'cash', label: '现金支付' }, { value: 'cash', label: '现金支付' },
{ value: 'scan_code', label: '扫码支付' }, { value: 'scan_code', label: '扫码支付' },
{ value: 'subscription', label: '订阅支付' }, { value: 'subscription', label: '订阅支付' },
{ value: 'wxpay_online', label: '微信在线代付' } { value: 'wxpay_online', label: '微信在线代付' },
{ value: 'client_wxpay', label: '客户自行付款' },
{ value: 'deposit', label: '定金' }
] ]
} }
@ -317,25 +341,31 @@ export default {
this.orderTypes = [ this.orderTypes = [
{ value: '1', label: '新订单' }, { value: '1', label: '新订单' },
{ value: '2', label: '续费订单' }, { value: '2', label: '续费订单' },
{ value: '3', label: '内部员工订单' } { value: '3', label: '内部员工订单' },
{ value: '4', label: '转校' },
{ value: '5', label: '客户内转课订单' }
] ]
} }
} catch (error) { } catch (error) {
console.error('加载字典数据失败:', error) console.error('加载字典数据失败:', error)
// 使 // 使
this.paymentTypes = [ this.paymentTypes = [
{ value: 'cash', label: '现金支付' }, { value: 'cash', label: '现金支付' },
{ value: 'scan_code', label: '扫码支付' }, { value: 'scan_code', label: '扫码支付' },
{ value: 'subscription', label: '订阅支付' }, { value: 'subscription', label: '订阅支付' },
{ value: 'wxpay_online', label: '微信在线代付' } { value: 'wxpay_online', label: '微信在线代付' },
{ value: 'client_wxpay', label: '客户自行付款' },
{ value: 'deposit', label: '定金' }
] ]
this.orderTypes = [ this.orderTypes = [
{ value: '1', label: '新订单' }, { value: '1', label: '新订单' },
{ value: '2', label: '续费订单' }, { value: '2', label: '续费订单' },
{ value: '3', label: '内部员工订单' } { value: '3', label: '内部员工订单' },
{ value: '4', label: '转校' },
{ value: '5', label: '客户内转课订单' }
] ]
} }
}, },
@ -380,6 +410,41 @@ export default {
this.giftList = [] this.giftList = []
} }
}, },
/**
* 加载班级列表
*/
async loadClassList() {
console.log('开始加载班级列表,校区ID:', this.studentInfo?.campus_id)
try {
if (!this.studentInfo?.campus_id) {
console.warn('缺少校区ID,无法加载班级列表')
this.classList = []
return
}
const res = await apiRoute.getClassListForSchedule({
campus_id: this.studentInfo.campus_id
})
console.log('班级列表API响应:', res)
if (res.code === 1) {
// 使 id class_name
this.classList = (res.data || []).map(cls => ({
value: cls.id,
label: cls.class_name || `班级${cls.id}`,
...cls
}))
console.log('班级列表加载成功:', this.classList)
} else {
console.error('获取班级列表失败:', res.msg)
this.classList = []
}
} catch (error) {
console.error('获取班级列表异常:', error)
this.classList = []
}
},
showCoursePicker() { showCoursePicker() {
this.currentPicker = 'course' this.currentPicker = 'course'
@ -462,7 +527,7 @@ export default {
this.currentPicker = 'giftType' this.currentPicker = 'giftType'
this.pickerTitle = '选择核销类型' this.pickerTitle = '选择核销类型'
this.pickerOptions = this.giftTypes this.pickerOptions = this.giftTypes
// //
if (this.formData.gift_type) { if (this.formData.gift_type) {
const currentIndex = this.giftTypes.findIndex(item => item.value === this.formData.gift_type) const currentIndex = this.giftTypes.findIndex(item => item.value === this.formData.gift_type)
@ -470,11 +535,34 @@ export default {
} else { } else {
this.giftTypeSelectedIndex = 0 this.giftTypeSelectedIndex = 0
} }
this.pickerValue = [this.giftTypeSelectedIndex] this.pickerValue = [this.giftTypeSelectedIndex]
this.selectedIndex = this.giftTypeSelectedIndex this.selectedIndex = this.giftTypeSelectedIndex
this.$refs.pickerPopup.open() this.$refs.pickerPopup.open()
}, },
showClassPicker() {
this.currentPicker = 'class'
this.pickerTitle = '选择班级'
// ""
this.pickerOptions = [
{ value: '', label: '不选择班级' },
...this.classList
]
//
if (this.formData.class_id) {
const currentIndex = this.pickerOptions.findIndex(item => item.value == this.formData.class_id)
this.classSelectedIndex = currentIndex >= 0 ? currentIndex : 0
} else {
this.classSelectedIndex = 0
}
this.pickerValue = [this.classSelectedIndex]
this.selectedIndex = this.classSelectedIndex
this.$refs.pickerPopup.open()
},
onPickerChange(e) { onPickerChange(e) {
this.selectedIndex = e.detail.value[0] this.selectedIndex = e.detail.value[0]
@ -523,6 +611,11 @@ export default {
this.formData.gift_type = selectedOption.value this.formData.gift_type = selectedOption.value
console.log('赠品类型选择后更新表单数据:', this.formData) console.log('赠品类型选择后更新表单数据:', this.formData)
break break
case 'class':
this.classSelectedIndex = this.selectedIndex
this.formData.class_id = selectedOption.value
console.log('班级选择后更新表单数据:', this.formData)
break
} }
this.closePicker() this.closePicker()

66
uniapp/components/schedule/ScheduleDetail.vue

@ -77,6 +77,10 @@
<view class="student-info"> <view class="student-info">
<view class="student-name">{{ student.name }}</view> <view class="student-name">{{ student.name }}</view>
<view class="student-age">年龄{{ student.age }}</view> <view class="student-age">年龄{{ student.age }}</view>
<view class="attendance-status">
<text class="status-label">签到状态</text>
<text :class="['status-text', getStatusClass(student.status)]">{{ getStatusText(student.status) }}</text>
</view>
<view class="course-status">课程状态{{ student.courseStatus }}</view> <view class="course-status">课程状态{{ student.courseStatus }}</view>
<view class="course-arrangement">课程安排{{ student.courseType === 'fixed' ? '固定课' : '临时课' }}</view> <view class="course-arrangement">课程安排{{ student.courseType === 'fixed' ? '固定课' : '临时课' }}</view>
@ -136,6 +140,10 @@
<view class="student-info"> <view class="student-info">
<view class="student-name">{{ student.name }}</view> <view class="student-name">{{ student.name }}</view>
<view class="student-age">年龄{{ student.age }}</view> <view class="student-age">年龄{{ student.age }}</view>
<view class="attendance-status">
<text class="status-label">签到状态</text>
<text :class="['status-text', getStatusClass(student.status)]">{{ getStatusText(student.status) }}</text>
</view>
<view class="course-status">课程状态{{ student.courseStatus }}</view> <view class="course-status">课程状态{{ student.courseStatus }}</view>
<view class="course-arrangement">课程安排等待位</view> <view class="course-arrangement">课程安排等待位</view>
@ -555,6 +563,16 @@
// //
handleStudentClick(student, index) { handleStudentClick(student, index) {
console.log('点击了学员:', student) console.log('点击了学员:', student)
// 0
if (student.status !== 0) {
uni.showToast({
title: `该学员已${this.getStatusText(student.status)},无法重复操作`,
icon: 'none',
duration: 2000
});
return;
}
// //
if (student.schedule_type === 2) { if (student.schedule_type === 2) {
// - // -
@ -854,6 +872,16 @@
return statusTextMap[status] || '未知状态'; return statusTextMap[status] || '未知状态';
}, },
//
getStatusClass(status) {
const statusClassMap = {
0: 'status-pending', //
1: 'status-completed', //
2: 'status-leave' //
};
return statusClassMap[status] || '';
},
// //
getStatusTextFromCode(statusCode) { getStatusTextFromCode(statusCode) {
const statusTextMap = { const statusTextMap = {
@ -1137,6 +1165,44 @@
color: #ff3b30; color: #ff3b30;
} }
/* 签到状态相关样式 */
.attendance-status {
display: flex;
align-items: center;
margin: 8rpx 0;
font-size: 24rpx;
}
.status-label {
color: #666;
margin-right: 8rpx;
}
.status-text {
font-weight: 600;
padding: 4rpx 12rpx;
border-radius: 12rpx;
font-size: 22rpx;
}
.status-text.status-pending {
background: rgba(255, 149, 0, 0.1);
color: #ff9500;
border: 1rpx solid rgba(255, 149, 0, 0.2);
}
.status-text.status-completed {
background: rgba(41, 211, 180, 0.1);
color: #29d3b4;
border: 1rpx solid rgba(41, 211, 180, 0.2);
}
.status-text.status-leave {
background: rgba(255, 59, 48, 0.1);
color: #ff3b30;
border: 1rpx solid rgba(255, 59, 48, 0.2);
}
.status-absent { .status-absent {
color: #8e8e93; color: #8e8e93;
} }

95
uniapp/pages-market/clue/clue_info.vue

@ -220,6 +220,7 @@
<script> <script>
import apiRoute from '@/api/apiRoute.js' import apiRoute from '@/api/apiRoute.js'
import dictUtilSimple from '@/common/dictUtilSimple.js'
// //
import ClientInfoCard from '@/components/client-info-card/client-info-card.vue' import ClientInfoCard from '@/components/client-info-card/client-info-card.vue'
import StudentInfoCard from '@/components/student-info-card/student-info-card.vue' import StudentInfoCard from '@/components/student-info-card/student-info-card.vue'
@ -290,6 +291,9 @@
showQRCodeModal: false, showQRCodeModal: false,
qrCodePaymentData: null, qrCodePaymentData: null,
//
paymentTypeDict: [], //
// //
remark_content: '', remark_content: '',
currentRecord: null, currentRecord: null,
@ -408,8 +412,82 @@
}, },
methods: { methods: {
/**
* 加载字典数据
*/
async loadDictData() {
try {
console.log('开始加载支付方式字典数据')
const dictResult = await dictUtilSimple.getBatchDict(['payment_type'])
if (dictResult.payment_type && Array.isArray(dictResult.payment_type)) {
this.paymentTypeDict = dictResult.payment_type
console.log('支付方式字典加载成功:', this.paymentTypeDict)
} else {
console.warn('支付方式字典数据格式不正确:', dictResult.payment_type)
// 使
this.paymentTypeDict = [{
name: '现金支付',
value: 'cash'
},
{
name: '扫码支付',
value: 'scan_code'
},
{
name: '订阅支付',
value: 'subscription'
},
{
name: '微信在线代付',
value: 'wxpay_online'
},
{
name: '客户自行付款',
value: 'client_wxpay'
},
{
name: '定金',
value: 'deposit'
}
]
}
} catch (error) {
console.error('加载支付方式字典失败:', error)
// 使
this.paymentTypeDict = [{
name: '现金支付',
value: 'cash'
},
{
name: '扫码支付',
value: 'scan_code'
},
{
name: '订阅支付',
value: 'subscription'
},
{
name: '微信在线代付',
value: 'wxpay_online'
},
{
name: '客户自行付款',
value: 'client_wxpay'
},
{
name: '定金',
value: 'deposit'
}
]
}
},
async init() { async init() {
try { try {
//
await this.loadDictData()
await this.getInfo() await this.getInfo()
await Promise.all([ await Promise.all([
this.getUserInfo(), this.getUserInfo(),
@ -1278,13 +1356,24 @@
// //
formatPaymentType(paymentType) { formatPaymentType(paymentType) {
const typeMap = { if (!paymentType) return '未知'
//
const paymentItem = this.paymentTypeDict.find(item => item.value === paymentType)
if (paymentItem) {
return paymentItem.name
}
// 使
const fallbackMap = {
'cash': '现金支付', 'cash': '现金支付',
'scan_code': '扫码支付', 'scan_code': '扫码支付',
'subscription': '订阅支付', 'subscription': '订阅支付',
'wxpay_online': '微信在线代付' 'wxpay_online': '微信在线代付',
'client_wxpay': '客户自行付款',
'deposit': '定金'
} }
return typeMap[paymentType] || paymentType || '未知' return fallbackMap[paymentType] || paymentType || '未知'
}, },
// //

2123
uniapp/pages/market/clue/add_clues.vue

File diff suppressed because it is too large

623
uniapp/pages/market/clue/class_arrangement.vue

@ -1,623 +0,0 @@
<template>
<view class="class-arrange-root">
<!-- 顶部日期选择 -->
<view class="date-bar">
<view class="date-item" v-for="(item, idx) in weekList" :key="idx" :class="{active: item.status}" @click="getDate(item.date,item.day)">
<view class="week">{{ item.week }}</view>
<view class="day">{{ item.day }}</view>
</view>
</view>
<!-- "查询"按钮移到日期条下方 -->
<view class="more-bar-wrapper">
<view class="more-bar" @click="openSearchPopup">
<text>查询</text>
<uni-icons type="arrowdown" size="18" color="#bdbdbd" />
</view>
</view>
<!-- 日历底部弹窗 -->
<uni-popup ref="calendarPopup" type="bottom">
<uni-calendar @change="onCalendarChange" />
</uni-popup>
<!-- 查询弹窗 -->
<view v-if="showSearchPopup" class="search_popup_mask" @tap="showSearchPopup=false">
<view class="search_popup_content" @tap.stop>
<view class="popup_search_content">
<view class="popup_header">
<view class="popup_title">筛选</view>
<view class="popup_close" @tap="showSearchPopup=false">
<text class="close_text"></text>
</view>
</view>
<scroll-view :scroll-y="true" class="popup_scroll_view">
<!-- 筛选区域 -->
<view class="popup_filter_section">
<view class="popup_filter_row">
<view class="popup_filter_item">
<text class="popup_filter_label">时间查询</text>
<picker :value="timeIndex" :range="timeOptions" @change="onTimeChange">
<view class="popup_filter_picker">{{ timeOptions[timeIndex] }}</view>
</picker>
</view>
<view class="popup_filter_item">
<text class="popup_filter_label">班主任筛选</text>
<input class="popup_filter_input" placeholder="人员名称筛选" v-model="searchForm.teacher_name" />
</view>
</view>
<view class="popup_filter_row">
<view class="popup_filter_item">
<text class="popup_filter_label">开始日期</text>
<picker mode="date" :value="searchForm.start_date" @change="onStartDateChange">
<view class="popup_filter_input">
{{ searchForm.start_date || '选择开始日期' }}
</view>
</picker>
</view>
<view class="popup_filter_item">
<text class="popup_filter_label">结束日期</text>
<picker mode="date" :value="searchForm.end_date" @change="onEndDateChange">
<view class="popup_filter_input">
{{ searchForm.end_date || '选择结束日期' }}
</view>
</picker>
</view>
</view>
<view class="popup_filter_row">
<view class="popup_filter_item">
<text class="popup_filter_label">固定位筛选</text>
<input class="popup_filter_input" placeholder="输入固定位编号" v-model="searchForm.venue_number" type="number" />
</view>
</view>
</view>
</scroll-view>
<view class="popup_filter_buttons">
<view class="popup_filter_btn reset_btn" @click="resetSearchOnly">重置</view>
<view class="popup_filter_btn search_btn" @click="searchDataAndClose">搜索</view>
<view class="popup_filter_btn close_btn" @click="closeSearchPopup">关闭</view>
</view>
</view>
</view>
</view>
<!-- 课程卡片列表 -->
<view class="course-list">
<view class="course-card" v-for="(course, idx) in courseList" :key="idx">
<view class="card-header">
<view class="status-end">{{ getStatusText(course.status) }}</view>
</view>
<view class="card-body">
<view class="row">时间{{ course.course_date || '未设置' }}</view>
<view class="row">校区{{ course.campus_name || '未设置' }}</view>
<view class="row">教室{{ course.venue ? course.venue.venue_name : '未设置' }}</view>
<view class="row">课程{{ course.course ? course.course.course_name : '未设置' }}</view>
<view class="row">人数{{ course.available_capacity || 0 }}</view>
<view class="row">安排情况{{ course.student ? course.student.length : 0 }}/{{course.max_students ? course.max_students : '不限'}}</view>
</view>
<view class="card-footer">
<button class="detail-btn" @click="viewDetail(course)">详情</button>
</view>
</view>
</view>
</view>
</template>
<script>
import apiRoute from '@/api/apiRoute.js';
export default {
data() {
return {
weekList: [],
selectedDayIndex: 4,
date: '',
courseList: [],
resource_id: '',
student_id: '',
//
showSearchPopup: false,
searchForm: {
time_hour: '',
start_date: '',
end_date: '',
teacher_name: '',
venue_number: ''
},
//
timeIndex: 0,
timeOptions: ['全部时间', '上午(8:00-12:00)', '下午(12:00-18:00)', '晚上(18:00-22:00)']
};
},
onLoad(options) {
this.resource_id = options.resource_id || '';
this.student_id = options.student_id || '';
this.getDate();
},
methods: {
async getDate(date = '', day = '') {
try {
let res = await apiRoute.getDate({
'date': date,
'day': day
})
this.weekList = res.data.dates
this.date = res.data.date
let data = await apiRoute.courseAllList({
'schedule_date': this.date
})
// courseList
this.courseList = Array.isArray(data.data) ? data.data : []
} catch (error) {
console.error('获取信息失败:', error);
// 使
this.courseList = [];
uni.showToast({
title: '获取课程数据失败',
icon: 'none'
});
}
},
openCalendar() {
this.$refs.calendarPopup.open();
},
closeCalendar() {
console.log(123123)
this.$refs.calendarPopup.close();
},
viewDetail(course) {
//
const resourceId = this.resource_id || '';
const studentId = this.student_id || '';
uni.navigateTo({
url: '/pages-market/clue/class_arrangement_detail?schedule_id=' + course.id + '&resource_id=' + resourceId + '&student_id=' + studentId
});
},
onCalendarConfirm(e) {
// e.fulldate
uni.showToast({
title: '选择日期:' + e.fulldate,
icon: 'none'
});
this.closeCalendar();
},
onCalendarChange(e) {
this.getDate(e.fulldate, e.date);
this.closeCalendar();
},
getStatusText(status) {
const statusMap = {
pending: '待开始',
upcoming: '即将开始',
ongoing: '进行中',
completed: '已结束'
};
return statusMap[status] || status;
},
//
openSearchPopup() {
this.showSearchPopup = true;
},
closeSearchPopup() {
this.showSearchPopup = false;
},
//
onTimeChange(e) {
this.timeIndex = e.detail.value;
this.searchForm.time_hour = this.timeOptions[this.timeIndex];
},
//
onStartDateChange(e) {
this.searchForm.start_date = e.detail.value;
console.log('开始日期选择:', e.detail.value);
},
//
onEndDateChange(e) {
this.searchForm.end_date = e.detail.value;
console.log('结束日期选择:', e.detail.value);
},
//
searchDataAndClose() {
console.log('执行搜索,表单数据:', this.searchForm);
this.searchCourseData();
this.showSearchPopup = false;
},
//
async searchCourseData() {
try {
let searchParams = {
schedule_date: this.date
};
//
if (this.searchForm.time_hour && this.searchForm.time_hour !== '全部时间') {
searchParams.time_hour = this.searchForm.time_hour;
}
if (this.searchForm.start_date) {
searchParams.start_date = this.searchForm.start_date;
}
if (this.searchForm.end_date) {
searchParams.end_date = this.searchForm.end_date;
}
if (this.searchForm.teacher_name) {
searchParams.teacher_name = this.searchForm.teacher_name;
}
if (this.searchForm.venue_number) {
searchParams.venue_number = this.searchForm.venue_number;
}
console.log('搜索参数:', searchParams);
let data = await apiRoute.courseAllList(searchParams);
// courseList
this.courseList = Array.isArray(data.data) ? data.data : [];
uni.showToast({
title: '查询完成',
icon: 'success'
});
} catch (error) {
console.error('搜索失败:', error);
uni.showToast({
title: '搜索失败',
icon: 'none'
});
}
},
//
resetSearchOnly() {
this.searchForm = {
time_hour: '',
start_date: '',
end_date: '',
teacher_name: '',
venue_number: ''
};
this.timeIndex = 0;
//
this.getDate();
},
// - 访
safeGet(obj, path, defaultValue = '') {
if (!obj) return defaultValue;
const keys = path.split('.');
let result = obj;
for (let key of keys) {
if (result === null || result === undefined || !result.hasOwnProperty(key)) {
return defaultValue;
}
result = result[key];
}
return result || defaultValue;
}
},
};
</script>
<style lang="less" scoped>
.class-arrange-root {
background: #232323;
min-height: 100vh;
padding-bottom: 30rpx;
}
.date-bar {
display: flex;
align-items: center;
background: #232323;
padding: 0 0 10rpx 0;
overflow-x: auto;
border-bottom: 1px solid #333;
.date-item {
flex: 1;
text-align: center;
color: #bdbdbd;
padding: 16rpx 0 0 0;
.week {
font-size: 22rpx;
}
.day {
font-size: 28rpx;
margin-top: 4rpx;
}
&.active {
color: #29d3b4;
.day {
border-radius: 50%;
background: #333;
color: #29d3b4;
padding: 2rpx 10rpx;
}
}
}
}
.more-bar-wrapper {
width: 100%;
display: flex;
justify-content: center;
margin: 10rpx 0 0 0;
}
.more-bar {
display: flex;
align-items: center;
color: #bdbdbd;
font-size: 22rpx;
cursor: pointer;
background: #333;
border-radius: 20rpx;
padding: 8rpx 24rpx;
}
.course-list {
margin-top: 20rpx;
.course-card {
background: #434544;
border-radius: 10rpx;
margin: 0 0 20rpx 0;
padding: 0 0 20rpx 0;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08);
.card-header {
display: flex;
justify-content: flex-end;
padding: 10rpx 20rpx 0 0;
.status-end {
background: #e95c6b;
color: #fff;
border-radius: 10rpx;
padding: 4rpx 18rpx;
font-size: 22rpx;
}
}
.card-body {
padding: 0 20rpx;
.row {
color: #fff;
font-size: 24rpx;
margin: 8rpx 0;
}
}
.card-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 20rpx;
.sign-info {
color: #bdbdbd;
font-size: 22rpx;
}
.detail-btn {
background: transparent;
border: 2rpx solid #ffd86b;
color: #ffd86b;
border-radius: 8rpx;
padding: 6rpx 24rpx;
font-size: 24rpx;
width: 100%;
margin-top: 40rpx;
}
}
}
}
//
.search_popup_mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.6);
z-index: 999;
display: flex;
flex-direction: column;
}
.search_popup_content {
background: #fff;
border-bottom-left-radius: 24rpx;
border-bottom-right-radius: 24rpx;
animation: slideDown 0.3s ease-out;
width: 100%;
max-height: 80vh;
overflow: hidden;
display: flex;
flex-direction: column;
}
@keyframes slideDown {
from {
transform: translateY(-100%);
}
to {
transform: translateY(0);
}
}
//
.popup_search_content {
padding: 0;
background: #fff;
min-height: 60vh;
max-height: 80vh;
display: flex;
flex-direction: column;
border-bottom-left-radius: 24rpx;
border-bottom-right-radius: 24rpx;
overflow: hidden;
}
.popup_header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 32rpx;
border-bottom: 1px solid #f0f0f0;
}
.popup_title {
font-size: 32rpx;
font-weight: 600;
color: #333;
}
.popup_close {
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
.close_text {
font-size: 32rpx;
color: #999;
}
}
.popup_scroll_view {
flex: 1;
padding: 32rpx;
overflow-y: auto;
}
.popup_filter_section {
margin-bottom: 32rpx;
&:last-child {
margin-bottom: 0;
}
}
.popup_filter_row {
display: flex;
gap: 20rpx;
margin-bottom: 24rpx;
&:last-child {
margin-bottom: 0;
}
}
.popup_filter_item {
flex: 1;
display: flex;
flex-direction: column;
gap: 12rpx;
&.full_width {
flex: 1;
}
.popup_filter_label {
font-size: 26rpx;
color: #666;
font-weight: 500;
}
.popup_filter_input {
height: 72rpx;
line-height: 72rpx;
padding: 0 16rpx;
border: 1px solid #ddd;
border-radius: 8rpx;
font-size: 28rpx;
color: #333;
background: #fff;
&::placeholder {
color: #999;
}
}
.popup_filter_picker {
height: 72rpx;
line-height: 72rpx;
padding: 0 16rpx;
border: 1px solid #ddd;
border-radius: 8rpx;
font-size: 28rpx;
color: #333;
background: #fff;
position: relative;
&::after {
content: '▼';
position: absolute;
right: 16rpx;
font-size: 20rpx;
color: #999;
}
}
}
.popup_filter_buttons {
display: flex;
gap: 20rpx;
padding: 32rpx;
margin-top: auto;
border-top: 1px solid #f0f0f0;
background: #fff;
border-bottom-left-radius: 24rpx;
border-bottom-right-radius: 24rpx;
}
.popup_filter_btn {
flex: 1;
height: 72rpx;
line-height: 72rpx;
text-align: center;
border-radius: 8rpx;
font-size: 28rpx;
font-weight: 600;
&.search_btn {
background: #29d3b4;
color: #fff;
}
&.reset_btn {
background: #f5f5f5;
color: #666;
border: 1px solid #ddd;
}
&.close_btn {
background: #666;
color: #fff;
}
}
</style>

1292
uniapp/pages/market/clue/class_arrangement_detail.vue

File diff suppressed because it is too large

431
uniapp/pages/market/clue/clue_info.less

@ -1,431 +0,0 @@
// 客户详情页样式文件
.assemble {
background-color: #292929;
min-height: 100vh;
}
.content {
padding: 20rpx;
}
.tab-switcher-container {
margin: 20rpx 0;
}
// 学生信息区域
.student-section {
margin-top: 20rpx;
.section-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx;
.section-title {
font-size: 32rpx;
font-weight: bold;
color: #fff;
}
.add-student-btn {
display: flex;
align-items: center;
background: #29d3b4;
color: #fff;
border-radius: 20rpx;
padding: 12rpx 20rpx;
font-size: 24rpx;
.add-icon {
margin-right: 8rpx;
font-weight: bold;
font-size: 20rpx;
}
&:active {
background: #1ea08e;
transform: scale(0.95);
}
}
}
.student-cards {
width: 100%;
min-height: 600rpx;
.student-swiper {
width: 100%;
height: 560rpx;
.student-swiper-content {
display: flex;
flex-direction: column;
height: 580rpx;
padding: 20rpx;
box-sizing: border-box;
background-color: #434544;
border-radius: 16rpx;
margin: 0 10rpx;
color: #fff;
}
}
}
}
.popup-footer {
display: flex;
padding: 30rpx 40rpx;
padding-bottom: calc(30rpx + env(safe-area-inset-bottom));
border-top: 1px solid #333;
gap: 30rpx;
flex-shrink: 0; /* 确保底部按钮区域不被压缩 */
background: #1a1a1a; /* 确保背景色一致 */
.popup-footer-btns {
display: flex;
justify-content: space-between;
.footer-btn {
flex: 1;
height: 88rpx;
display: flex;
width: 45%;
align-items: center;
justify-content: center;
border-radius: 12rpx;
font-size: 32rpx;
font-weight: 500;
&.cancel-btn {
background: #cccccc;
}
&.confirm-btn {
background: linear-gradient(45deg, #29D3B4, #1DB584);
color: #ffffff;
}
}
}
}
// 课程、通话、体测记录等区域
.course-section, .call-section, .fitness-section, .study-plan-section {
background-color: #434544;
border-radius: 16rpx;
margin: 20rpx 0;
.section-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx;
.context-title {
font-size: 28rpx;
font-weight: bold;
color: #fff;
}
.add-record-btn {
display: flex;
align-items: center;
background: #29d3b4;
color: #fff;
border-radius: 20rpx;
padding: 12rpx 20rpx;
font-size: 24rpx;
.add-icon {
margin-right: 8rpx;
font-weight: bold;
}
&:active {
background: #1ea08e;
transform: scale(0.95);
}
}
}
}
// 操作按钮区域 - 独立于Swiper外部
.student-section .action-buttons-section {
display: flex;
gap: 8rpx;
margin-top: 15rpx;
padding: 15rpx;
flex-wrap: nowrap;
height: 100rpx;
box-sizing: border-box;
overflow-x: auto;
overflow-y: hidden;
-webkit-overflow-scrolling: touch;
background-color: #434544;
border-radius: 16rpx;
margin: 15rpx 0;
&::-webkit-scrollbar {
display: none;
}
.action-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(41, 211, 180, 0.3);
border-radius: 8rpx;
padding: 12rpx 8rpx;
min-width: 90rpx;
flex-shrink: 0;
transition: all 0.3s ease;
.action-icon {
font-size: 24rpx;
margin-bottom: 5rpx;
line-height: 1;
}
.action-text {
font-size: 18rpx;
color: #fff;
text-align: center;
line-height: 1.2;
word-break: break-all;
}
&:active {
background: rgba(41, 211, 180, 0.2);
border-color: #29d3b4;
transform: scale(0.95);
.action-text {
color: #29d3b4;
}
}
}
}
// 通用空状态样式
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60rpx 20rpx;
min-height: 200rpx;
.empty-icon {
font-size: 60rpx;
opacity: 0.6;
margin-bottom: 20rpx;
}
.empty-text {
color: #ccc;
font-size: 28rpx;
margin-bottom: 20rpx;
}
.empty-add-btn {
background: #29d3b4;
color: #fff;
padding: 12rpx 24rpx;
border-radius: 20rpx;
font-size: 24rpx;
transition: all 0.3s ease;
&:active {
background: #1ea08e;
transform: scale(0.95);
}
}
}
// 备注弹窗样式
.remark-dialog {
width: 600rpx;
padding: 30rpx;
background: #fff;
border-radius: 16rpx;
textarea {
width: 100%;
height: 200rpx;
padding: 20rpx;
border: 2rpx solid #e9ecef;
border-radius: 12rpx;
font-size: 28rpx;
color: #333;
background-color: #fff;
box-sizing: border-box;
resize: none;
&::placeholder {
color: #999;
}
&:focus {
border-color: #29d3b4;
outline: none;
box-shadow: 0 0 0 2rpx rgba(41, 211, 180, 0.1);
}
}
.dialog-btns {
display: flex;
gap: 20rpx;
margin-top: 30rpx;
.btn {
flex: 1;
padding: 20rpx;
border-radius: 8rpx;
font-size: 28rpx;
text-align: center;
&.cancel {
background: #f8f9fa;
color: #666;
border: 2rpx solid #e9ecef;
}
&.confirm {
background: #29d3b4;
color: #fff;
}
}
}
}
// 二维码支付弹窗样式
.qrcode-payment-modal {
width: 600rpx;
background: #fff;
border-radius: 20rpx;
overflow: hidden;
.modal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 40rpx 40rpx 20rpx;
border-bottom: 1px solid #f0f0f0;
.modal-title {
font-size: 36rpx;
font-weight: 600;
color: #333;
}
.close-btn {
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background: #f5f5f5;
color: #666;
font-size: 32rpx;
&:active {
background: #e5e5e5;
transform: scale(0.95);
}
}
}
.order-info {
padding: 30rpx 40rpx;
.info-row {
display: flex;
align-items: center;
margin-bottom: 20rpx;
.label {
font-size: 28rpx;
color: #666;
margin-right: 20rpx;
}
.value {
font-size: 28rpx;
color: #333;
flex: 1;
}
.amount {
font-size: 32rpx;
color: #ff4757;
font-weight: 600;
flex: 1;
}
}
}
.qrcode-container {
display: flex;
flex-direction: column;
align-items: center;
padding: 40rpx;
background: #fafafa;
.qrcode-image {
width: 300rpx;
height: 300rpx;
border: 1px solid #e5e5e5;
border-radius: 12rpx;
background: #fff;
margin-bottom: 20rpx;
}
.qrcode-tip {
font-size: 24rpx;
color: #666;
text-align: center;
line-height: 1.4;
}
}
.modal-buttons {
display: flex;
padding: 30rpx 40rpx 40rpx;
gap: 20rpx;
.btn {
flex: 1;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 40rpx;
font-size: 28rpx;
font-weight: 500;
&.secondary {
background: #f5f5f5;
color: #666;
&:active {
background: #e5e5e5;
transform: scale(0.98);
}
}
&.primary {
background: #29d3b4;
color: #fff;
&:active {
background: #1ea08e;
transform: scale(0.98);
}
}
}
}
}

1567
uniapp/pages/market/clue/clue_info.vue

File diff suppressed because it is too large

164
uniapp/pages/market/clue/clue_table.vue

@ -1,164 +0,0 @@
<template>
<view class="dark-table-container">
<view v-for="(row, rIdx) in data" :key="rIdx" class="card">
<view class="card-title" @click="toggleExpand(rIdx)">
{{ row.channel }}
<text class="expand-icon">{{ expanded[rIdx] ? '▲' : '▼' }}</text>
</view>
<view class="card-content">
<view
class="card-row"
v-for="(col, cIdx) in getVisibleColumns(rIdx)"
:key="cIdx"
>
<view class="card-label">
{{ col.label }}
<text v-if="col.tip" class="tip" @click.stop="showTip(col.tip)">?</text>
</view>
<view class="card-value">{{ row[col.key] || '-' }}</view>
</view>
</view>
<view v-if="columns.length > 6" class="expand-btn" @click="toggleExpand(rIdx)">
{{ expanded[rIdx] ? '收起' : '展开全部' }}
</view>
</view>
<!-- 说明弹窗 -->
<uni-popup ref="popup" type="center">
<view class="popup-content">{{ tipContent }}</view>
</uni-popup>
<AQTabber></AQTabber>
</view>
</template>
<script>
import AQTabber from "@/components/AQ/AQTabber.vue"
export default {
components: {
AQTabber,
},
data() {
return {
columns: [
{ label: '渠道', key: 'channel' },
{ label: '资源数', key: 'resource', tip: '本月的新资源' },
{ label: '到访数(一访)', key: 'visit1', tip: '本月资源的一访到访数' },
{ label: '到访率(一访)', key: 'visitRate1', tip: '一访到访数/本月新资源数' },
{ label: '到访数(二访)', key: 'visit2', tip: '二访到访数/一访到访数' },
{ label: '到访率(二访)', key: 'visitRate2', tip: '二访到访数/一访到访数' },
{ label: '关单数(一访)', key: 'close1', tip: '本月一访到访的关单数' },
{ label: '关单率(一访)', key: 'closeRate1', tip: '一访关单数/一访到访数' },
{ label: '关单数(二访)', key: 'close2', tip: '二访到访的关单数' },
{ label: '关单率(二访)', key: 'closeRate2', tip: '二访关单数/二访到访数' },
{ label: '月总转', key: 'monthTrans', tip: '关单数(合计)/资源数' },
{ label: '往月到访数', key: 'lastMonthVisit', tip: '本月资源在往月到访的到访数' },
{ label: '月共到访', key: 'monthTotalVisit', tip: '本月资源任在本月到访/关单' },
{ label: '往月关单数', key: 'lastMonthClose', tip: '本月关单-往月关单' },
{ label: '月共关单', key: 'monthTotalClose', tip: '本月关单+往月关单' },
],
data: [
{ channel: '体检包(地推)', resource: 10, visit1: 5, visitRate1: '50%', visit2: 2, visitRate2: '40%', close1: 1, closeRate1: '20%', close2: 1, closeRate2: '50%', monthTrans: '10%', lastMonthVisit: 0, monthTotalVisit: 5, lastMonthClose: 0, monthTotalClose: 1 },
],
tipContent: '',
expanded: {},
}
},
methods: {
showTip(content) {
this.tipContent = content
this.$refs.popup.open()
},
toggleExpand(idx) {
this.$set(this.expanded, idx, !this.expanded[idx])
},
getVisibleColumns(idx) {
// 5
const cols = this.columns.slice(1)
if (this.expanded[idx]) return cols
return cols.slice(0, 5)
}
}
}
</script>
<style scoped>
.dark-table-container {
background: #18181c;
color: #f1f1f1;
min-height: 100vh;
padding: 24rpx 12rpx;
}
.card {
background: #23232a;
border-radius: 18rpx;
box-shadow: 0 4rpx 24rpx rgba(0,0,0,0.18);
margin-bottom: 32rpx;
padding: 24rpx 20rpx 8rpx 20rpx;
transition: box-shadow 0.2s;
}
.card-title {
font-size: 32rpx;
font-weight: bold;
margin-bottom: 18rpx;
color: #ffb300;
letter-spacing: 2rpx;
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
}
.expand-icon {
font-size: 24rpx;
color: #bdbdbd;
margin-left: 12rpx;
}
.card-content {
display: flex;
flex-direction: column;
gap: 12rpx;
overflow: hidden;
transition: max-height 0.3s;
}
.card-row {
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px dashed #333;
padding: 8rpx 0;
}
.card-label {
color: #bdbdbd;
font-size: 26rpx;
display: flex;
align-items: center;
}
.card-value {
color: #f1f1f1;
font-size: 26rpx;
text-align: right;
max-width: 60%;
word-break: break-all;
}
.tip {
color: #ffb300;
margin-left: 8rpx;
font-size: 24rpx;
cursor: pointer;
}
.expand-btn {
color: #ffb300;
text-align: center;
font-size: 26rpx;
margin: 10rpx 0 0 0;
padding-bottom: 8rpx;
cursor: pointer;
}
.popup-content {
background: #23232a;
color: #fff;
padding: 32rpx;
border-radius: 16rpx;
min-width: 300rpx;
text-align: center;
}
</style>

1806
uniapp/pages/market/clue/edit_clues.vue

File diff suppressed because it is too large

365
uniapp/pages/market/clue/edit_clues_log.vue

@ -1,365 +0,0 @@
<!--编辑客户-->
<template>
<view class="assemble">
<!--切换-->
<view class="tab_section">
<fui-segmented-control
:values="optionTable"
:current="(Number(optionTableId))"
type="button"
radius="8"
height="80"
color="#29d3b4"
bold="true"
@click="segmented">
</fui-segmented-control>
</view>
<!-- 客户资源修改记录-->
<scroll-view
v-show="filteredData.type == `resource`"
scroll-y="true"
:lower-threshold="lowerThreshold"
@scrolltolower="loadMoreData"
style="margin-top:40rpx;height: 80vh;"
>
<!--时间轴-->
<view class="table_list">
<fui-timeaxis background="#292929" :padding="['36rpx','16rpx']">
<fui-timeaxis-node
:lineColor="k<3 ? v.activeColor:'#ccc'"
:lined="k!==tableList.length-1"
v-for="(v,k) in tableList"
:key="k"
>
<!--inco-->
<view
class="fui-node"
:style="{borderColor:(k != tableList.length-1 ? '#29d3b4' :'' ) }"
>
</view>
<template v-slot:right>
<view
class="fui-process__node table_itme"
:style="{paddingBottom:(k !== tableList.length-1 ? '50rpx' :'0rpx')}"
:class="{'fui-node__pb':k!==tableList.length-1}"
>
<view class="fui-title">
<!--修改时间-->
{{v.modification_time}}
</view>
<view class="itme_box">
<view class="title_name">修改人{{v.staff_id_name}} 修改了如下内容</view>
<view class="table_section">
<table class="table_box">
<!-- 自定义表头 -->
<thead>
<tr>
<th style="width: 30%; border: 1px solid #ddd; padding: 12rpx;">字段名称</th>
<th style="width: 34%; border: 1px solid #ddd; padding: 12rpx;">原数据</th>
<th style="width: 34%; border: 1px solid #ddd; padding: 12rpx;">新数据</th>
</tr>
</thead>
<!-- 自定义每一行数据 -->
<tbody>
<tr v-for="(row, index) in v.update_arr" :key="index">
<td style="width: 30%; border: 1px solid #ddd; padding: 12rpx;">{{ row.field_name_zh }}</td>
<td style="width: 34%; border: 1px solid #ddd; padding: 12rpx;">{{ row.old_value }}</td>
<td style="width: 34%; border: 1px solid #ddd; padding: 12rpx;">{{ row.new_value }}</td>
</tr>
</tbody>
</table>
</view>
</view>
</view>
</template>
</fui-timeaxis-node>
</fui-timeaxis>
</view>
</scroll-view>
<!-- 六要素修改记录-->
<scroll-view
v-show="filteredData.type == `six_speed`"
scroll-y="true"
:lower-threshold="lowerThreshold"
@scrolltolower="loadMoreData"
style="margin-top:40rpx;height: 80vh;"
>
<!--时间轴-->
<view class="table_list">
<fui-timeaxis background="#292929" :padding="['36rpx','16rpx']">
<fui-timeaxis-node
:lineColor="k<3?v.activeColor:'#ccc'"
:lined="k!==tableList.length-1"
v-for="(v,k) in tableList"
:key="k"
>
<!--inco-->
<view
class="fui-node"
:style="{borderColor:(k != tableList.length-1 ? '#29d3b4' :'' ) }"
>
</view>
<template v-slot:right>
<view
class="fui-process__node table_itme"
:style="{paddingBottom:(k !== tableList.length-1 ? '50rpx' :'0rpx')}"
:class="{'fui-node__pb':k!==tableList.length-1}"
>
<view class="fui-title">
<!--修改时间-->
{{v.modification_time}}
</view>
<view class="itme_box">
<view class="title_name">修改人{{v.staff_id_name}} 修改了如下内容</view>
<view class="table_section">
<table class="table_box">
<!-- 自定义表头 -->
<thead>
<tr>
<th style="width: 30%; border: 1px solid #ddd; padding: 12rpx;">字段名称</th>
<th style="width: 34%; border: 1px solid #ddd; padding: 12rpx;">原数据</th>
<th style="width: 34%; border: 1px solid #ddd; padding: 12rpx;">新数据</th>
</tr>
</thead>
<!-- 自定义每一行数据 -->
<tbody>
<tr v-for="(row, index) in v.update_arr" :key="index">
<td style="width: 30%; border: 1px solid #ddd; padding: 12rpx;">{{ row.field_name_zh }}</td>
<td style="width: 34%; border: 1px solid #ddd; padding: 12rpx;">{{ row.old_value }}</td>
<td style="width: 34%; border: 1px solid #ddd; padding: 12rpx;">{{ row.new_value }}</td>
</tr>
</tbody>
</table>
</view>
</view>
</view>
</template>
</fui-timeaxis-node>
</fui-timeaxis>
</view>
</scroll-view>
</view>
</template>
<script>
import apiRoute from '@/api/apiRoute.js';
export default {
data() {
return {
loading:false,//
lowerThreshold: 100,//
isReachedBottom: false,//|true=|false=
//
filteredData:{
page:1,//
limit:10,//
total:10,//
type:'resource',//|resource=,six_speed=
customer_resource_id: '',//id
},
//
tableList:[],//
//tab
optionTableId:0,//
optionTable:[
{
id: 0,
name: '客户资源修改记录',
type:'resource',
},
{
id: 1,
name: '六要素修改记录',
type:'six_speed',
}
]
}
},
onLoad(options) {
this.filteredData.customer_resource_id = options.resource_id//id
},
onShow() {
this.init()
},
//
async onPullDownRefresh() {
//
await this.resetFilteredData()
await this.getList()
},
methods: {
//
async init() {
await this.getList()//
},
//()
loadMoreData() {
//
if (!this.isReachedBottom) {
this.isReachedBottom = true;//
this.getList();
}
},
//
async resetFilteredData() {
this.isReachedBottom = false; // 便
this.filteredData.page = 1//
this.filteredData.limit = 10//
this.filteredData.total = 10//
},
//-
async getList(){
this.loading = true
let data = {...this.filteredData}
//
if ((this.filteredData.page - 1) * this.filteredData.limit >= this.filteredData.total) {
this.loading = false
uni.showToast({
title: '暂无更多',
icon: 'none'
})
return
}
if(data.page == 1){
this.tableList = []
}
let res = await apiRoute.xs_customerResourcesGetEditLogList(data)
this.loading = false
this.isReachedBottom = false;
if (res.code != 1){
uni.showToast({
title: res.msg,
icon: 'none'
})
return
}
console.log(123123123,res)
this.tableList = this.tableList.concat(res.data.data); // 使 concat
console.log('列表1',this.tableList)
this.filteredData.total = res.data.total
this.filteredData.page++
},
//tag
async segmented(e) {
console.log('切换',e)
//e.id|0= 1=
let status = e.id
this.optionTableId = String(status)
this.filteredData.type = e.type//|resource=,six_speed=
await this.resetFilteredData()
await this.getList()
},
}
}
</script>
<style lang="less" scoped>
.assemble{
width: 100%;
height: 100%;
background-color: #292929;
overflow: auto;
.tab_section{
padding: 0 20rpx;
padding-top: 30rpx;
}
.table_list{
color: #fff;
::v-deep .fui-timeaxis__line{
background-color: #fff;
}
.itme_box {
margin-top: 30rpx;
display: flex;
flex-direction: column;
gap: 25rpx;
.title_name{
font-size: 28rpx;
}
.table_section{
.table_box{
width: 100%;
border-collapse: collapse;
table-layout: fixed;
tr{
display: flex;
th{}
td{}
}
}
}
}
::v-deep .fui-timeaxis__wrap{
}
.fui-node {
width: 32rpx;
height: 32rpx;
border-radius: 50%;
background: #fff;
display: flex;
align-items: center;
justify-content: center;
border: 4px solid #ccc;
box-sizing: border-box;
}
.fui-process__node {
padding-left: 20rpx;
}
.fui-title {
font-size: 34rpx;
line-height: 34rpx;
font-weight: bold;
}
.fui-descr {
font-size: 28rpx;
padding-top: 12rpx;
color: #7F7F7F;
}
.fui-time__left {
font-size: 36rpx;
line-height: 36rpx;
text-align: right;
padding-right: 20rpx;
}
.fui-node__pbtm {
padding-bottom: 88rpx;
}
}
}
</style>

1749
uniapp/pages/market/clue/index.vue

File diff suppressed because it is too large

343
uniapp/pages/market/data/statistics.vue

@ -1,343 +0,0 @@
<!--市场数据统计页面-->
<template>
<view class="main_box" :class="{'has-safe-area': hasSafeArea}">
<!--自定义导航栏-->
<view class="navbar_section">
<view class="title">市场数据统计</view>
</view>
<!-- 顶部筛选 -->
<view class="filter-section">
<view class="filter-item">
<view class="label">统计时间</view>
<picker mode="date" fields="month" :value="currentDate" @change="dateChange">
<view class="picker-value">{{currentDate}}</view>
</picker>
</view>
</view>
<!-- 市场人员资源量统计 -->
<view class="table-section">
<view class="table-title">市场人员资源量统计</view>
<view class="table-container">
<view class="table-header">
<view class="th th-person">人员</view>
<view class="th th-week">周数量</view>
<view class="th th-month">月数量</view>
<view class="th th-year">年数量</view>
</view>
<view class="table-body">
<view class="table-row" v-for="(item, index) in staffData" :key="index">
<view class="td td-person">{{item.name}}</view>
<view class="td td-week">{{item.weekCount}}</view>
<view class="td td-month">{{item.monthCount}}</view>
<view class="td td-year">{{item.yearCount}}</view>
</view>
</view>
</view>
</view>
<!-- 校区人员资源渠道量 -->
<view class="table-section">
<view class="table-title">{{currentMonth}} - 年度校区人员资源渠道量</view>
<view class="table-container">
<view class="table-header">
<view class="th th-channel" style="width: 100px;">渠道</view>
<view class="th" v-for="(school, index) in schoolList" :key="index">{{school.campus_name}}</view>
<view class="th th-total">渠道合计</view>
</view>
<view class="table-body">
<view class="table-row" v-for="(channel, index) in channelList" :key="index">
<view class="td td-channel" style="width: 100px;">{{channel.name}}</view>
<view class="td" v-for="(school, sIndex) in schoolList" :key="sIndex">
{{channel[school.id] || 0}}
</view>
<view class="td td-total">{{channel.total}}</view>
</view>
</view>
</view>
</view>
<!-- 市场人员资源渠道统计 -->
<view class="table-section">
<view class="table-title">{{currentMonth}} - 年度市场人员资源渠道统计</view>
<view class="table-container">
<view class="table-header">
<view class="th th-channel" style="width: 100px;">渠道</view>
<view class="th" v-for="(staff, index) in staffData" :key="index">市场{{staff.name}}</view>
<view class="th th-total">资源合计</view>
</view>
<view class="table-body">
<view class="table-row" v-for="(channel, index) in channelList" :key="index">
<view class="td td-channel" style="width: 100px;">{{channel.name}}</view>
<view class="td" v-for="(staff, sIndex) in staffData" :key="sIndex">
{{staff['channel'][channel.value] || 0}}
</view>
<view class="td td-total">{{channel.total}}</view>
</view>
</view>
</view>
</view>
<AQTabber />
</view>
</template>
<script>
import apiRoute from '@/api/apiRoute.js';
import AQTabber from "@/components/AQ/AQTabber.vue"
export default {
components: {
AQTabber,
},
data() {
return {
currentDate: this.formatDate(new Date()),
currentMonth: new Date().getMonth() + 1,
//
staffData: [],
//
schoolList: [],
//
hasSafeArea: false,
//
channelList: [],
//
marketStaffList: [
{ id: 'A', name: 'A' },
{ id: 'B', name: 'B' },
{ id: 'C', name: 'C' },
{ id: 'D', name: 'D' }
],
//
channelSchoolData: [],
//
channelStaffData: []
}
},
onLoad() {
this.initData();
this.checkSafeArea();
},
methods: {
// YYYY-MM
formatDate(date) {
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
return `${year}-${month}`;
},
//
dateChange(e) {
this.currentDate = e.detail.value;
const [year, month] = this.currentDate.split('-');
this.currentMonth = parseInt(month);
this.initData();
},
//
async initData() {
// API
await this.getStaffStatistics();
// await this.getChannelSchoolStatistics();
// this.getChannelStaffStatistics();
},
//
async getStaffStatistics() {
// API
let res = await apiRoute.getStaffStatistics({date: this.currentDate})
console.log(res.data.staffData,"================");
this.staffData = res.data.staffData
this.schoolList = res.data.schoolList
this.channelList = res.data.channelList
},
//
getChannelSchoolStatistics() {
// API
this.channelSchoolData = this.channelList.map(channel => {
const schools = {};
let total = 0;
this.schoolList.forEach(school => {
const count = Math.floor(Math.random() * 50);
schools[school.id] = count;
total += count;
});
return {
id: channel.id,
name: channel.name,
schools,
total
}
});
},
//
getChannelStaffStatistics() {
// API
this.channelStaffData = this.channelList.map(channel => {
const staffs = {};
let total = 0;
this.marketStaffList.forEach(staff => {
const count = Math.floor(Math.random() * 40);
staffs[staff.id] = count;
total += count;
});
return {
id: channel.id,
name: channel.name,
staffs,
total
}
});
},
//
checkSafeArea() {
try {
const systemInfo = uni.getSystemInfoSync();
// iPhone X
if (systemInfo.safeAreaInsets && systemInfo.safeAreaInsets.bottom > 0) {
this.hasSafeArea = true;
}
} catch (e) {
console.error('获取系统信息失败', e);
}
}
}
}
</script>
<style lang="scss" scoped>
.main_box {
min-height: 100vh;
background-color: #1a1a1a;
color: #ffffff;
padding-bottom: 150rpx; /* 增加底部内边距,防止内容被底部导航栏遮挡 */
}
/* 适配有安全区域的设备(如iPhone X及以上机型) */
.has-safe-area {
padding-bottom: calc(150rpx + constant(safe-area-inset-bottom)); /* iOS 11.0-11.2 */
padding-bottom: calc(150rpx + env(safe-area-inset-bottom)); /* iOS 11.2+ */
}
.navbar_section {
background-color: #1a1a1a;
height: 88rpx;
display: flex;
align-items: center;
justify-content: center;
position: relative;
.title {
font-size: 36rpx;
font-weight: bold;
}
}
.filter-section {
padding: 20rpx;
background-color: #222222;
margin: 20rpx;
border-radius: 10rpx;
.filter-item {
display: flex;
align-items: center;
.label {
font-size: 28rpx;
margin-right: 10rpx;
}
.picker-value {
padding: 10rpx 20rpx;
background-color: #333333;
border-radius: 6rpx;
font-size: 28rpx;
}
}
}
.table-section {
margin: 30rpx 20rpx;
background-color: #222222;
border-radius: 10rpx;
overflow: hidden;
.table-title {
font-size: 32rpx;
font-weight: bold;
padding: 20rpx;
border-bottom: 1px solid #333333;
}
.table-container {
width: 100%;
overflow-x: auto;
}
.table-header {
display: flex;
background-color: #333333;
.th {
padding: 15rpx 10rpx;
font-size: 26rpx;
text-align: center;
flex: 1;
min-width: 100rpx;
font-weight: bold;
}
}
.table-body {
.table-row {
display: flex;
border-bottom: 1px solid #333333;
&:last-child {
border-bottom: none;
}
.td {
padding: 15rpx 10rpx;
font-size: 26rpx;
text-align: center;
flex: 1;
min-width: 100rpx;
}
}
}
.th-person, .td-person {
min-width: 80rpx;
flex: 0.8;
}
.th-week, .td-week,
.th-month, .td-month,
.th-year, .td-year {
flex: 1;
}
.th-channel, .td-channel {
flex: 1.2;
text-align: left;
padding-left: 20rpx;
}
.th-total, .td-total {
flex: 1.2;
font-weight: bold;
}
}
</style>

427
uniapp/pages/market/index/index.vue

@ -1,427 +0,0 @@
<template>
<view class="assemble">
<view style="height: 20rpx;"></view>
<!-- 时间筛选 -->
<view class="filter-section">
<picker mode="date" fields="month" :value="currentDate" @change="onDateChange">
<view class="date-picker">
<text>{{currentDate}}</text>
<image class="drop-image" :src="$util.img('/static/images/drop.png')" mode="aspectFit"></image>
</view>
</picker>
</view>
<!-- 本月提成卡片 -->
<view class="commission-card">
<view class="card-title">本月提成</view>
<view class="commission-amount">¥{{totalCommission}}</view>
</view>
<!-- 续费提成记录 -->
<view class="record-card">
<view class="card-title">续费提成</view>
<view class="table">
<view class="table-header">
<view class="th">时间</view>
<view class="th">到期数</view>
<view class="th">续费数</view>
<view class="th">续费率</view>
<view class="th">提成</view>
</view>
<view class="table-body">
<view class="tr" v-for="(item, index) in renewalRecords" :key="index">
<view class="td">{{item.time}}</view>
<view class="td">{{item.expireCount}}</view>
<view class="td">{{item.renewCount}}</view>
<view class="td">{{item.renewRate}}%</view>
<view class="td">¥{{item.commission}}</view>
</view>
</view>
</view>
</view>
<!-- 新招课包 -->
<view class="record-card">
<view class="card-title">新招课包</view>
<view class="table">
<view class="table-header">
<view class="th">成交数</view>
<view class="th">提成</view>
<view class="th">合计</view>
</view>
<view class="table-body">
<view class="tr">
<view class="td">{{newPackageCount}}</view>
<view class="td">¥{{newPackageCommission}}</view>
<view class="td">¥{{newPackageTotal}}</view>
</view>
</view>
</view>
</view>
<!-- 私教课包 -->
<view class="record-card">
<view class="card-title">私教课包</view>
<view class="table">
<view class="table-header">
<view class="th">数量</view>
<view class="th">提成</view>
</view>
<view class="table-body">
<view class="tr">
<view class="td">{{privatePackageCount}}</view>
<view class="td">¥{{privatePackageCommission}}</view>
</view>
</view>
</view>
</view>
<!-- 时间卡 -->
<view class="record-card">
<view class="card-title">时间卡</view>
<view class="table">
<view class="table-header">
<view class="th">时间卡</view>
<view class="th">数量</view>
<view class="th">提成</view>
</view>
<view class="table-body">
<view class="tr">
<view class="td">{{timeCardCount}}</view>
<view class="td">{{timeCardAmount}}</view>
<view class="td">¥{{timeCardCommission}}</view>
</view>
</view>
</view>
</view>
<!-- 其他提成 -->
<view class="record-card">
<view class="card-title">其他提成</view>
<view class="table">
<view class="table-header">
<view class="th">项目</view>
<view class="th">金额</view>
</view>
<view class="table-body">
<view class="tr" v-for="(item, index) in otherCommissions" :key="index">
<view class="td">{{item.project}}</view>
<view class="td">¥{{item.amount}}</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import apiRoute from '@/api/apiRoute.js'
export default {
data() {
return {
currentDate: this.formatDate(new Date()),
totalCommission: 0,
renewalRecords: [],
newPackageCount: 0,
newPackageCommission: 0,
newPackageTotal: 0,
privatePackageCount: 0,
privatePackageCommission: 0,
timeCardCount: 0,
timeCardAmount: 0,
timeCardCommission: 0,
otherCommissions: []
}
},
onShow() {
this.getStatisticsData()
},
methods: {
formatDate(date) {
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
return `${year}-${month}`
},
onDateChange(e) {
this.currentDate = e.detail.value
this.getStatisticsData()
},
async getStatisticsData() {
try {
const res = await apiRoute.xs_statisticsMarketData({
date: this.currentDate
})
if (res && res.code === 1) {
const data = res.data
this.totalCommission = data.totalCommission || 0
this.renewalRecords = data.renewalRecords || []
this.newPackageCount = data.newPackageCount || 0
this.newPackageCommission = data.newPackageCommission || 0
this.newPackageTotal = data.newPackageTotal || 0
this.privatePackageCount = data.privatePackageCount || 0
this.privatePackageCommission = data.privatePackageCommission || 0
this.timeCardCount = data.timeCardCount || 0
this.timeCardAmount = data.timeCardAmount || 0
this.timeCardCommission = data.timeCardCommission || 0
this.otherCommissions = data.otherCommissions || []
}
} catch (error) {
console.error('获取统计数据失败:', error)
uni.showToast({
title: '获取数据失败',
icon: 'none'
})
}
}
}
}
</script>
<style lang="less" scoped>
//
.navbar_section {
border: 1px solid #23262F;
display: flex;
justify-content: center;
align-items: center;
background: #23262F;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.18);
.title {
padding: 40rpx 0rpx;
// #ifdef MP-WEIXIN
padding: 80rpx 0rpx;
// #endif
font-size: 30rpx;
color: #F5F6FA;
font-weight: bold;
letter-spacing: 2rpx;
}
}
.assemble {
width: 100%;
min-height: 100vh;
background: #181A20;
padding-bottom: 100rpx;
}
.div-style {
width: 92%;
height: 85vh;
background: #fff;
border-radius: 16rpx;
margin: auto;
}
.coach-message {
width: 92%;
margin: 10rpx auto;
display: flex;
align-items: center;
padding-top: 20rpx;
}
.drop-image {
width: 50rpx;
height: 50rpx;
filter: brightness(0.8);
}
.title {
font-size: 30rpx;
color: #F5F6FA;
padding-left: 20rpx;
}
.left1 {
width: 48%;
height: 95%;
margin: auto;
}
.right1 {
width: 48%;
height: 95%;
margin: auto;
.statistics_box {
margin: auto;
margin-top: 10rpx;
display: flex;
justify-content: space-between;
.item {
width: 90rpx;
display: flex;
flex-direction: column;
align-items: center;
.box {
width: 100%;
height: 328rpx;
border: 1px solid #ddd;
border-radius: 6rpx;
background: #f5f5f5;
position: relative;
.progress-bar {
width: 100%;
height: 0;
transition: height 0.3s ease;
position: absolute;
bottom: 0;
}
.ratio {
width: 100%;
position: absolute;
bottom: -0rpx;
font-size: 26rpx;
text-align: center;
}
}
.title {
margin-top: 5rpx;
padding: 0;
font-size: 26rpx;
color: #999999;
;
text-align: center;
}
}
}
}
.this_month {
width: 100%;
height: 95%;
margin: auto;
}
.drop-image-x {
width: 20rpx;
height: 20rpx;
}
.title-x {
font-size: 28rpx;
color: #7F7F7F;
padding-left: 20rpx;
}
.title-x1 {
font-size: 28rpx;
color: #333333;
padding-left: 60rpx;
}
.filter-section {
padding: 20rpx 30rpx;
background: #23262F;
margin-bottom: 20rpx;
border-radius: 12rpx;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.2);
.date-picker {
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
color: #F5F6FA;
.drop-image {
width: 30rpx;
height: 30rpx;
margin-left: 10rpx;
filter: brightness(0.8);
}
}
}
.commission-card {
background: linear-gradient(135deg, #FF6B6B, #FF8E8E);
margin: 20rpx;
padding: 30rpx;
border-radius: 16rpx;
color: #fff;
box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.25);
.card-title {
font-size: 28rpx;
margin-bottom: 20rpx;
color: #fff;
}
.commission-amount {
font-size: 48rpx;
font-weight: bold;
color: #fff;
}
}
.record-card {
background: #23262F;
margin: 20rpx;
padding: 20rpx;
border-radius: 16rpx;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.18);
.card-title {
font-size: 30rpx;
color: #F5F6FA;
margin-bottom: 20rpx;
font-weight: bold;
}
.table {
width: 100%;
.table-header {
display: flex;
background: #181A20;
padding: 20rpx 0;
border-radius: 8rpx 8rpx 0 0;
.th {
flex: 1;
text-align: center;
font-size: 26rpx;
color: #A0A3B1;
font-weight: 500;
}
}
.table-body {
.tr {
display: flex;
padding: 20rpx 0;
border-bottom: 1px solid #33343A;
transition: background 0.2s;
&:last-child {
border-bottom: none;
}
.td {
flex: 1;
text-align: center;
font-size: 26rpx;
color: #F5F6FA;
font-weight: 400;
}
&:hover {
background: #23262F;
}
}
}
}
}
//
::-webkit-scrollbar {
display: none;
}
</style>

495
uniapp/pages/market/my/campus_data.vue

@ -1,495 +0,0 @@
<!--校区数据-->
<template>
<view class="main_box">
<!--自定义导航栏-->
<view class="navbar_section">
<view class="navbar_content">
<view class="back_btn" @click="goBack">
<fui-icon name="arrowleft" size="32" color="#fff"></fui-icon>
</view>
<view class="title">校区数据</view>
<view class="placeholder"></view>
</view>
</view>
<view class="content_section">
<!-- 校区选择器 -->
<view class="campus_selector">
<view class="selector_label">选择校区</view>
<view class="selector_box">
<view class="selected_campus">请选择校区</view>
<fui-icon name="dropdown" size="24" color="#999"></fui-icon>
</view>
</view>
<!-- 数据概览卡片 -->
<view class="overview_cards">
<view class="card_item">
<view class="card_icon">
<fui-icon name="home" size="40" color="#29D3B4"></fui-icon>
</view>
<view class="card_content">
<view class="card_title">部门数量</view>
<view class="card_value">--</view>
</view>
</view>
<view class="card_item">
<view class="card_icon">
<fui-icon name="addressbook" size="40" color="#29D3B4"></fui-icon>
</view>
<view class="card_content">
<view class="card_title">员工总数</view>
<view class="card_value">--</view>
</view>
</view>
<view class="card_item">
<view class="card_icon">
<fui-icon name="star" size="40" color="#29D3B4"></fui-icon>
</view>
<view class="card_content">
<view class="card_title">客户总数</view>
<view class="card_value">--</view>
</view>
</view>
<view class="card_item">
<view class="card_icon">
<fui-icon name="wallet" size="40" color="#29D3B4"></fui-icon>
</view>
<view class="card_content">
<view class="card_title">校区业绩</view>
<view class="card_value">--</view>
</view>
</view>
</view>
<!-- 部门对比 -->
<view class="dept_comparison">
<view class="section_title">部门业绩对比</view>
<view class="comparison_list">
<view class="comparison_item">
<view class="dept_info">
<view class="dept_name">销售部</view>
<view class="dept_score">120000</view>
</view>
<view class="progress_bar">
<view class="progress_fill" style="width: 80%"></view>
</view>
<view class="dept_percent">80%</view>
</view>
<view class="comparison_item">
<view class="dept_info">
<view class="dept_name">市场部</view>
<view class="dept_score">100000</view>
</view>
<view class="progress_bar">
<view class="progress_fill" style="width: 67%"></view>
</view>
<view class="dept_percent">67%</view>
</view>
<view class="comparison_item">
<view class="dept_info">
<view class="dept_name">运营部</view>
<view class="dept_score">80000</view>
</view>
<view class="progress_bar">
<view class="progress_fill" style="width: 53%"></view>
</view>
<view class="dept_percent">53%</view>
</view>
<view class="comparison_item">
<view class="dept_info">
<view class="dept_name">客服部</view>
<view class="dept_score">60000</view>
</view>
<view class="progress_bar">
<view class="progress_fill" style="width: 40%"></view>
</view>
<view class="dept_percent">40%</view>
</view>
</view>
</view>
<!-- 月度趋势 -->
<view class="trend_section">
<view class="section_title">月度趋势</view>
<view class="trend_chart">
<view class="chart_placeholder">
<fui-icon name="linechart" size="80" color="#ddd"></fui-icon>
<view class="placeholder_text">图表功能待开发</view>
</view>
</view>
</view>
<!-- 功能按钮区域 -->
<view class="function_section">
<view class="section_title">数据分析</view>
<view class="function_grid">
<view class="function_item">
<view class="function_icon">
<fui-icon name="barchart" size="32" color="#29D3B4"></fui-icon>
</view>
<view class="function_text">校区对比</view>
</view>
<view class="function_item">
<view class="function_icon">
<fui-icon name="piechart" size="32" color="#29D3B4"></fui-icon>
</view>
<view class="function_text">部门分析</view>
</view>
<view class="function_item">
<view class="function_icon">
<fui-icon name="linechart" size="32" color="#29D3B4"></fui-icon>
</view>
<view class="function_text">趋势分析</view>
</view>
<view class="function_item">
<view class="function_icon">
<fui-icon name="list" size="32" color="#29D3B4"></fui-icon>
</view>
<view class="function_text">详细报表</view>
</view>
</view>
</view>
<!-- 提示信息 -->
<view class="tips_section">
<view class="tips_title">功能说明</view>
<view class="tips_content">
这里将显示校区的各项数据统计包括部门数量员工总数客户数量校区业绩等
具体功能待后续开发实现
</view>
</view>
</view>
</view>
</template>
<script>
import fuiIcon from "@/components/firstui/fui-icon/fui-icon.vue"
export default {
components: {
fuiIcon,
},
data() {
return {
}
},
onLoad() {
},
methods: {
//
goBack() {
uni.navigateBack()
},
}
}
</script>
<style lang="less" scoped>
.main_box {
background: #f5f5f5;
min-height: 100vh;
}
//
.navbar_section {
background: #29D3B4;
padding-top: 20rpx;
//
// #ifdef MP-WEIXIN
padding-top: 90rpx;
// #endif
.navbar_content {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx 24rpx 40rpx 24rpx;
.back_btn {
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
}
.title {
font-size: 32rpx;
color: #fff;
font-weight: 500;
}
.placeholder {
width: 60rpx;
}
}
}
//
.content_section {
padding: 40rpx 24rpx;
//
.campus_selector {
background: #fff;
border-radius: 16rpx;
padding: 32rpx 24rpx;
margin-bottom: 32rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
.selector_label {
font-size: 28rpx;
color: #333;
margin-bottom: 16rpx;
font-weight: 500;
}
.selector_box {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx 24rpx;
background: #f8f8f8;
border-radius: 12rpx;
.selected_campus {
font-size: 26rpx;
color: #666;
}
}
}
//
.overview_cards {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 24rpx;
margin-bottom: 40rpx;
.card_item {
background: #fff;
border-radius: 16rpx;
padding: 32rpx 24rpx;
display: flex;
align-items: center;
gap: 20rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
.card_icon {
width: 64rpx;
height: 64rpx;
background: rgba(41, 211, 180, 0.1);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.card_content {
flex: 1;
.card_title {
font-size: 24rpx;
color: #999;
margin-bottom: 8rpx;
}
.card_value {
font-size: 32rpx;
color: #333;
font-weight: 600;
}
}
}
}
//
.dept_comparison {
margin-bottom: 40rpx;
.section_title {
font-size: 28rpx;
color: #333;
font-weight: 600;
margin-bottom: 24rpx;
}
.comparison_list {
background: #fff;
border-radius: 16rpx;
padding: 32rpx 24rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
.comparison_item {
display: flex;
align-items: center;
margin-bottom: 32rpx;
&:last-child {
margin-bottom: 0;
}
.dept_info {
width: 150rpx;
.dept_name {
font-size: 26rpx;
color: #333;
margin-bottom: 8rpx;
font-weight: 500;
}
.dept_score {
font-size: 22rpx;
color: #29D3B4;
font-weight: 600;
}
}
.progress_bar {
flex: 1;
height: 16rpx;
background: #f0f0f0;
border-radius: 8rpx;
margin: 0 20rpx;
overflow: hidden;
.progress_fill {
height: 100%;
background: linear-gradient(90deg, #29D3B4, #5CE1E6);
border-radius: 8rpx;
transition: width 0.3s ease;
}
}
.dept_percent {
width: 60rpx;
text-align: right;
font-size: 24rpx;
color: #29D3B4;
font-weight: 600;
}
}
}
}
//
.trend_section {
margin-bottom: 40rpx;
.section_title {
font-size: 28rpx;
color: #333;
font-weight: 600;
margin-bottom: 24rpx;
}
.trend_chart {
background: #fff;
border-radius: 16rpx;
padding: 40rpx 24rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
.chart_placeholder {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 200rpx;
.placeholder_text {
font-size: 24rpx;
color: #999;
margin-top: 16rpx;
}
}
}
}
//
.function_section {
margin-bottom: 40rpx;
.section_title {
font-size: 28rpx;
color: #333;
font-weight: 600;
margin-bottom: 24rpx;
}
.function_grid {
background: #fff;
border-radius: 16rpx;
padding: 32rpx 24rpx;
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 32rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
.function_item {
display: flex;
flex-direction: column;
align-items: center;
padding: 24rpx 16rpx;
border-radius: 12rpx;
transition: all 0.3s ease;
&:active {
background-color: #f5f5f5;
transform: scale(0.95);
}
.function_icon {
width: 64rpx;
height: 64rpx;
background: rgba(41, 211, 180, 0.1);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 16rpx;
}
.function_text {
font-size: 24rpx;
color: #333;
}
}
}
}
//
.tips_section {
background: #fff;
border-radius: 16rpx;
padding: 32rpx 24rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
.tips_title {
font-size: 28rpx;
color: #333;
font-weight: 600;
margin-bottom: 16rpx;
}
.tips_content {
font-size: 24rpx;
color: #666;
line-height: 1.6;
}
}
}
</style>

445
uniapp/pages/market/my/dept_data.vue

@ -1,445 +0,0 @@
<!--部门数据-->
<template>
<view class="main_box">
<!--自定义导航栏-->
<view class="navbar_section">
<view class="navbar_content">
<view class="back_btn" @click="goBack">
<fui-icon name="arrowleft" size="32" color="#fff"></fui-icon>
</view>
<view class="title">部门数据</view>
<view class="placeholder"></view>
</view>
</view>
<view class="content_section">
<!-- 部门选择器 -->
<view class="dept_selector">
<view class="selector_label">选择部门</view>
<view class="selector_box">
<view class="selected_dept">请选择部门</view>
<fui-icon name="dropdown" size="24" color="#999"></fui-icon>
</view>
</view>
<!-- 数据概览卡片 -->
<view class="overview_cards">
<view class="card_item">
<view class="card_icon">
<fui-icon name="addressbook" size="40" color="#29D3B4"></fui-icon>
</view>
<view class="card_content">
<view class="card_title">部门人数</view>
<view class="card_value">--</view>
</view>
</view>
<view class="card_item">
<view class="card_icon">
<fui-icon name="star" size="40" color="#29D3B4"></fui-icon>
</view>
<view class="card_content">
<view class="card_title">总客户数</view>
<view class="card_value">--</view>
</view>
</view>
<view class="card_item">
<view class="card_icon">
<fui-icon name="wallet" size="40" color="#29D3B4"></fui-icon>
</view>
<view class="card_content">
<view class="card_title">部门业绩</view>
<view class="card_value">--</view>
</view>
</view>
<view class="card_item">
<view class="card_icon">
<fui-icon name="check" size="40" color="#29D3B4"></fui-icon>
</view>
<view class="card_content">
<view class="card_title">完成率</view>
<view class="card_value">--</view>
</view>
</view>
</view>
<!-- 排行榜 -->
<view class="ranking_section">
<view class="section_title">部门排行榜</view>
<view class="ranking_list">
<view class="ranking_item">
<view class="rank_number first">1</view>
<view class="member_info">
<view class="member_name">张三</view>
<view class="member_score">业绩50000</view>
</view>
<view class="medal">
<fui-icon name="star-fill" size="24" color="#FFD700"></fui-icon>
</view>
</view>
<view class="ranking_item">
<view class="rank_number second">2</view>
<view class="member_info">
<view class="member_name">李四</view>
<view class="member_score">业绩45000</view>
</view>
<view class="medal">
<fui-icon name="star-fill" size="24" color="#C0C0C0"></fui-icon>
</view>
</view>
<view class="ranking_item">
<view class="rank_number third">3</view>
<view class="member_info">
<view class="member_name">王五</view>
<view class="member_score">业绩40000</view>
</view>
<view class="medal">
<fui-icon name="star-fill" size="24" color="#CD7F32"></fui-icon>
</view>
</view>
</view>
</view>
<!-- 功能按钮区域 -->
<view class="function_section">
<view class="section_title">数据分析</view>
<view class="function_grid">
<view class="function_item">
<view class="function_icon">
<fui-icon name="barchart" size="32" color="#29D3B4"></fui-icon>
</view>
<view class="function_text">业绩对比</view>
</view>
<view class="function_item">
<view class="function_icon">
<fui-icon name="piechart" size="32" color="#29D3B4"></fui-icon>
</view>
<view class="function_text">人员分布</view>
</view>
<view class="function_item">
<view class="function_icon">
<fui-icon name="linechart" size="32" color="#29D3B4"></fui-icon>
</view>
<view class="function_text">趋势分析</view>
</view>
<view class="function_item">
<view class="function_icon">
<fui-icon name="list" size="32" color="#29D3B4"></fui-icon>
</view>
<view class="function_text">详细报表</view>
</view>
</view>
</view>
<!-- 提示信息 -->
<view class="tips_section">
<view class="tips_title">功能说明</view>
<view class="tips_content">
这里将显示部门的各项数据统计包括部门人员客户数量销售业绩排行榜等
具体功能待后续开发实现
</view>
</view>
</view>
</view>
</template>
<script>
import fuiIcon from "@/components/firstui/fui-icon/fui-icon.vue"
export default {
components: {
fuiIcon,
},
data() {
return {
}
},
onLoad() {
},
methods: {
//
goBack() {
uni.navigateBack()
},
}
}
</script>
<style lang="less" scoped>
.main_box {
background: #f5f5f5;
min-height: 100vh;
}
//
.navbar_section {
background: #29D3B4;
padding-top: 20rpx;
//
// #ifdef MP-WEIXIN
padding-top: 90rpx;
// #endif
.navbar_content {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx 24rpx 40rpx 24rpx;
.back_btn {
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
}
.title {
font-size: 32rpx;
color: #fff;
font-weight: 500;
}
.placeholder {
width: 60rpx;
}
}
}
//
.content_section {
padding: 40rpx 24rpx;
//
.dept_selector {
background: #fff;
border-radius: 16rpx;
padding: 32rpx 24rpx;
margin-bottom: 32rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
.selector_label {
font-size: 28rpx;
color: #333;
margin-bottom: 16rpx;
font-weight: 500;
}
.selector_box {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx 24rpx;
background: #f8f8f8;
border-radius: 12rpx;
.selected_dept {
font-size: 26rpx;
color: #666;
}
}
}
//
.overview_cards {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 24rpx;
margin-bottom: 40rpx;
.card_item {
background: #fff;
border-radius: 16rpx;
padding: 32rpx 24rpx;
display: flex;
align-items: center;
gap: 20rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
.card_icon {
width: 64rpx;
height: 64rpx;
background: rgba(41, 211, 180, 0.1);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.card_content {
flex: 1;
.card_title {
font-size: 24rpx;
color: #999;
margin-bottom: 8rpx;
}
.card_value {
font-size: 32rpx;
color: #333;
font-weight: 600;
}
}
}
}
//
.ranking_section {
margin-bottom: 40rpx;
.section_title {
font-size: 28rpx;
color: #333;
font-weight: 600;
margin-bottom: 24rpx;
}
.ranking_list {
background: #fff;
border-radius: 16rpx;
padding: 24rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
.ranking_item {
display: flex;
align-items: center;
padding: 20rpx 16rpx;
border-bottom: 1px solid #f0f0f0;
&:last-child {
border-bottom: none;
}
.rank_number {
width: 48rpx;
height: 48rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 24rpx;
font-weight: 600;
color: #fff;
margin-right: 24rpx;
&.first {
background: #FFD700;
}
&.second {
background: #C0C0C0;
}
&.third {
background: #CD7F32;
}
}
.member_info {
flex: 1;
.member_name {
font-size: 28rpx;
color: #333;
margin-bottom: 8rpx;
font-weight: 500;
}
.member_score {
font-size: 24rpx;
color: #666;
}
}
.medal {
margin-left: 16rpx;
}
}
}
}
//
.function_section {
margin-bottom: 40rpx;
.section_title {
font-size: 28rpx;
color: #333;
font-weight: 600;
margin-bottom: 24rpx;
}
.function_grid {
background: #fff;
border-radius: 16rpx;
padding: 32rpx 24rpx;
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 32rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
.function_item {
display: flex;
flex-direction: column;
align-items: center;
padding: 24rpx 16rpx;
border-radius: 12rpx;
transition: all 0.3s ease;
&:active {
background-color: #f5f5f5;
transform: scale(0.95);
}
.function_icon {
width: 64rpx;
height: 64rpx;
background: rgba(41, 211, 180, 0.1);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 16rpx;
}
.function_text {
font-size: 24rpx;
color: #333;
}
}
}
}
//
.tips_section {
background: #fff;
border-radius: 16rpx;
padding: 32rpx 24rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
.tips_title {
font-size: 28rpx;
color: #333;
font-weight: 600;
margin-bottom: 16rpx;
}
.tips_content {
font-size: 24rpx;
color: #666;
line-height: 1.6;
}
}
}
</style>

523
uniapp/pages/market/my/index.vue

@ -1,523 +0,0 @@
<!--销售我的-首页-->
<template>
<view class="main_box">
<!--自定义导航栏-->
<view class="navbar_section">
<view class="title">我的</view>
</view>
<view style="background:#29D3B4;">
<!--用户信息-->
<view class="user_section">
<view class="box">
<view class="left" @click="openViewMyInfo()">
<image class="pic" :src="userInfo.head_img"></image>
<view class="name">{{userInfo.name}}</view>
</view>
<view class="right">
<view class="btn"></view>
<!-- <view class="btn">切换身份</view>-->
<view class="btn"></view>
</view>
</view>
<view class="bottom" v-for="(v,k) in userInfo.cameus_dept_arr">
<view class="left">
<view class="title">校区</view>
<view class="title">{{v.campus_id_name || ''}}</view>
</view>
<view class="division"></view>
<view class="right">
<view class="title">部门</view>
<view class="title dept">{{v.dept_name_str || ''}}</view>
</view>
</view>
</view>
</view>
<view class="main_section">
<view class="grid_container">
<view class="grid_item" @click="openViewReimbursementList()">
<view class="icon_wrapper">
<fui-icon name="bankcard" size="48" color="#29D3B4"></fui-icon>
</view>
<view class="item_text">报销记录</view>
</view>
<view class="grid_item" @click="openViewMyAttendance()">
<view class="icon_wrapper">
<fui-icon name="calendar" size="48" color="#29D3B4"></fui-icon>
</view>
<view class="item_text">我的考勤</view>
</view>
<view class="grid_item" @click="goCourseSchedule()">
<view class="icon_wrapper">
<fui-icon name="list" size="48" color="#29D3B4"></fui-icon>
</view>
<view class="item_text">课程安排</view>
</view>
<view class="grid_item" @click="openViewMyMessage()">
<view class="icon_wrapper">
<fui-icon name="message" size="48" color="#29D3B4"></fui-icon>
</view>
<view class="item_text">我的消息</view>
</view>
<view class="grid_item" @click="my_contract()">
<view class="icon_wrapper">
<fui-icon name="order" size="48" color="#29D3B4"></fui-icon>
</view>
<view class="item_text">我的合同</view>
</view>
<view class="grid_item" @click="openMyData()">
<view class="icon_wrapper">
<fui-icon name="barchart" size="48" color="#29D3B4"></fui-icon>
</view>
<view class="item_text">我的数据</view>
</view>
<view class="grid_item" @click="openDeptData()">
<view class="icon_wrapper">
<fui-icon name="piechart" size="48" color="#29D3B4"></fui-icon>
</view>
<view class="item_text">部门数据</view>
</view>
<view class="grid_item" @click="openCampusData()">
<view class="icon_wrapper">
<fui-icon name="linechart" size="48" color="#29D3B4"></fui-icon>
</view>
<view class="item_text">校区数据</view>
</view>
<view class="grid_item" @click="openViewSetUp()">
<view class="icon_wrapper">
<fui-icon name="setup" size="48" color="#29D3B4"></fui-icon>
</view>
<view class="item_text">设置</view>
</view>
</view>
</view>
<!-- 底部导航-->
<AQTabber/>
</view>
</template>
<script>
import marketApi from '@/api/market.js';
import apiRoute from '@/api/apiRoute.js';
import {
Api_url
} from "@/common/config.js";
import AQTabber from "@/components/AQ/AQTabber.vue"
import fuiIcon from "@/components/firstui/fui-icon/fui-icon.vue"
export default {
components: {
AQTabber,
fuiIcon,
},
data() {
return {
formData:{},
userInfo:{},//
//APi
uploadUrl: `${Api_url}/file/image`,
signedClientListCount:0,//
}
},
onLoad() {
},
onShow() {
this.init();
},
methods: {
//
async init(){
await this.getUserInfo()//
// await this.getSignedClientListCount()//
},
//
async getSignedClientListCount(){
let data = {
page:1,
limit:1,
}
let res = await marketApi.signClient(data);
if (res.code != 1){
uni.showToast({
title: res.msg,
icon: 'none'
})
return
}
this.signedClientListCount = res.data.total
},
//
async getUserInfo(){
let data = {}
let res = await apiRoute.getPersonnelInfo(data);
if (res.code != 1){
uni.showToast({
title: res.msg,
icon: 'none'
})
return
}
res.data.cameus_dept_arr.forEach((v,k)=>{
let d_arr = []
v.dept_arr.forEach((dv,dk)=>{
d_arr.push(dv.dept_name)
})
//
v.dept_name_str = d_arr.join(',')
})
this.userInfo = res.data
},
//
openViewArrivalStatistics(){
uni.navigateTo({
url: '/pages-market/my/arrival_statistics'
})
},
//
openViewDueSoon(){
uni.navigateTo({
url: '/pages-market/my/due_soon'
})
},
//
openViewSchoolingStatistics(){
uni.navigateTo({
url: '/pages-market/my/schooling_statistics'
})
},
//
openViewFeedback(){
uni.navigateTo({
url: '/pages-common/feedback'
})
},
//
openViewMyInfo(){
uni.navigateTo({
url: '/pages-market/my/info'
})
},
//-
openViewSignedClientList(){
uni.navigateTo({
url: '/pages-market/my/signed_client_list'
})
},
//-
openViewMyAttendance(){
uni.navigateTo({
url: '/pages-common/my_attendance'
})
},
//
openViewFirmInfo(){
uni.navigateTo({
url: '/pages-market/my/firm_info'
})
},
//
openViewSetUp(){
uni.navigateTo({
url: '/pages-market/my/set_up'
})
},
//-
openViewMyMessage(){
uni.navigateTo({
url: '/pages-common/my_message'
})
},
//-
openViewReimbursementList(){
uni.navigateTo({
url: '/pages-market/reimbursement/list'
})
},
goCourseSchedule(){
uni.navigateTo({
url: '/pages-coach/coach/schedule/schedule_table'
})
},
my_contract(){
uni.navigateTo({
url: '/pages-common/contract/my_contract'
})
},
//
openMyData(){
uni.navigateTo({
url: '/pages-market/my/my_data'
})
},
//
openDeptData(){
uni.navigateTo({
url: '/pages-market/my/dept_data'
})
},
//
openCampusData(){
uni.navigateTo({
url: '/pages-market/my/campus_data'
})
},
}
}
</script>
<style lang="less" scoped>
.main_box{
background: #292929;
//min-height: 28vh;
min-height: 100%;
}
//
.navbar_section{
border: 1px solid #29D3B4;
display: flex;
justify-content: center;
align-items: center;
background: #29D3B4;
.title{
padding: 40rpx 0rpx;
/* 小程序端样式 */
// #ifdef MP-WEIXIN
padding-top: 110rpx;
padding-bottom: 40rpx;
// #endif
font-size: 30rpx;
color: #fff;
}
}
//
.user_section {
background-color: #29D3B4;
padding-top: 10rpx;
padding-bottom: 42rpx;
color: #fff;
font-size: 28rpx;
.box{
padding-left: 19rpx;
padding-right: 29rpx;
display: flex;
justify-content: space-between;
align-items: center;
gap: 15rpx;
.left{
display: flex;
align-items: center;
gap: 20rpx;
.pic{
width: 144rpx;
height: 144rpx;
border-radius: 50%;
}
.name{
font-size: 28rpx;
}
}
.right{
display: flex;
flex-direction: column;
gap: 20rpx;
.btn{
min-height: 28rpx;
font-size: 28rpx;
}
}
}
.bottom{
margin-top: 30rpx;
padding: 0rpx 40rpx;
display: flex;
justify-content: space-between;
align-items: flex-start;
.title{
font-size: 28rpx;
}
//线
.division{
width: 2px;
height: 35rpx;
background-color: #fff;
}
.left{
width: 48%;
display: flex;
}
.right{
width: 48%;
display: flex;
.dept{
width: 70%;
}
}
}
}
//
.count_section{
position: relative;
.main{
position: relative;
z-index: 2;
padding: 0rpx 24rpx;
display: flex;
justify-content: center;
.course_box{
padding: 42rpx 28rpx;
width: 692rpx;
border-radius: 20rpx;
background-color: #fff;
display: flex;
flex-direction: column;
gap: 32rpx;
.top{
display: flex;
justify-content: space-between;
align-items: center;
.item{
display: flex;
flex-direction: column;
align-items: center;
gap: 12rpx;
.num{
color: #29D3B4;
font-size: 56rpx;
}
.intro{
color: #AAAAAA;
font-size: 24rpx;
}
}
}
.bottom{
font-size: 24rpx;
color: #333333;
text{
margin-left: 10rpx;
color: #29D3B4;
}
.reduce{
color: #ef95a0;
}
}
}
}
.bg_box{
z-index: 1;
width: 100%;
height: 150rpx;
}
.bg_top{
position: absolute;
top: 0;
background-color: #29D3B4;
}
.bg_bottom{
top: 50%;
position: absolute;
background-color: #292929;
}
}
.main_section{
background: #292929 100%;
padding: 0 24rpx;
padding-top: 40rpx;
padding-bottom: 150rpx;
font-size: 24rpx;
color: #333333;
.grid_container {
background: #fff;
border-radius: 16rpx;
padding: 40rpx 24rpx;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 40rpx 20rpx;
.grid_item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20rpx 10rpx;
border-radius: 12rpx;
transition: all 0.3s ease;
&:active {
background-color: #f5f5f5;
transform: scale(0.95);
}
.icon_wrapper {
width: 80rpx;
height: 80rpx;
background: rgba(41, 211, 180, 0.1);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 16rpx;
}
.item_text {
font-size: 24rpx;
color: #333333;
text-align: center;
font-weight: 400;
line-height: 1.2;
}
}
}
}
</style>

505
uniapp/pages/market/my/info.vue

@ -1,505 +0,0 @@
<!--销售-个人资料-详情-->
<template>
<view class="main_box">
<view class="main_section">
<view class="section">
<view class="item">
<image
@click="changeAvatar()"
class="pic"
:src="$util.img(formData.head_img)"
></image>
<view class="btn" @click="changeAvatar()">修改头像</view>
</view>
</view>
<view class="section">
<view class="item">
<view class="title">
姓名 <text class="required">*</text>
</view>
<view class="input">
<input v-model="formData.name" placeholder="请输入姓名" />
</view>
</view>
<view class="item">
<view class="title">
账号 <text class="required"></text>
</view>
<view class="input">
<input v-model="formData.username" disabled placeholder="暂无" />
</view>
</view>
<view class="item">
<view class="title">
部门 <text class="required"></text>
</view>
<view class="input">
<!-- <input disabled :placeholder="formData.department_name_str" />-->
<view class="dept disabled">{{formData.department_name_str || '暂无'}}</view>
</view>
</view>
<view class="item">
<view class="title">
等级 <text class="required"></text>
</view>
<view class="input">
<input v-model="formData.member_level_name" disabled placeholder="暂无" />
</view>
</view>
</view>
<view class="section">
<view class="item">
<view class="title">
性别 <text class="required">*</text>
</view>
<view class="input">
<input placeholder="请选择性别" v-model="formData.gender_str" @click="picker_show_sex=true"/>
<fui-picker
layer="1"
:linkage="true"
:options="options_sex_arr"
:show="picker_show_sex"
@change="changePickerSex"
@cancel="picker_show_sex=false"
></fui-picker>
</view>
</view>
<view class="item">
<view class="title">
生日 <text class="required">*</text>
</view>
<view class="input">
<input placeholder="请选择生日" @click="picker_show_birthday=true" v-model="formData.birthday"/>
<fui-date-picker
:minDate="minDate"
:maxDate="maxDate"
:show="picker_show_birthday"
type="3"
@change="changePickerBirthday"
@cancel="picker_show_birthday=false"
></fui-date-picker>
</view>
</view>
<view class="item">
<view class="title">
邮箱 <text class="required">*</text>
</view>
<view class="input">
<input v-model="formData.email" placeholder="请输入邮箱" />
</view>
</view>
<view class="item">
<view class="title">
手机 <text class="required">*</text>
</view>
<view class="input">
<input v-model="formData.phone" placeholder="请输入手机" />
</view>
</view>
<view class="item">
<view class="title">
微信 <text class="required"></text>
</view>
<view class="input">
<input v-model="formData.wx" placeholder="请输入微信" />
</view>
</view>
</view>
<view class="submet_btn" @click="submit">提交</view>
</view>
</view>
</template>
<script>
import apiRoute from '@/api/apiRoute.js';
import marketApi from '@/api/market.js';
import {
Api_url
} from "@/common/config.js";
import AQTabber from "@/components/AQ/AQTabber"
export default {
components: {
AQTabber,
},
data() {
return {
formData:{
head_img:'',//
name:'',//
username:'',//
address:'',//
gender:'',//|1,2
gender_str:'',
birthday:'',//
email:'',//
phone:'',//
wx:'',//
},
userInfo: {},
//APi
uploadUrl: `${Api_url}/uploadImage`,
//
picker_show_sex: false,
sex_name:'请选择',
options_sex_arr: [
{
value: 1,
text: '男'
},
{
value: 2,
text: '女'
},
],
//
minDate: '',
maxDate: '',
picker_show_birthday: false,
upload_type: 1,
uploadHeadimg: '',
editHeadimg: '',
}
},
onLoad() {
},
onShow() {
this.init()
},
methods: {
async init(){
// this.getBirthday()
this.setDateYear()
await this.getUserInfo()
},
//
setDateYear() {
let currentYear = new Date().getFullYear();
this.minDate = String(currentYear - 100);
this.maxDate = String(currentYear + 1);
},
//
async getUserInfo(){
let res = await apiRoute.getPersonnelInfo({})
if (res.code != 1){
uni.showToast({
title: res.msg,
icon: 'none'
})
return
}
let gender_str = ''
if(res.data.gender == 1){
gender_str = '男'
}else if(res.data.gender == 2){
gender_str = '女'
}
//
this.formData = {
head_img: res.data.head_img,//
name: res.data.name,//
username: res.data.phone,//
address: res.data.address,//
gender: res.data.gender,//|1,2
gender_str:gender_str,
birthday: res.data.birthday,//
email: res.data.email || '',//
phone: res.data.phone,//
wx: res.data.wx || '',//
member_level_name: res.data.member_level_name || '',//
department_name_str:res.data.department_name_str || '暂无',//
}
},
//
changeAvatar() {
uni.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
const tempFilePath = res.tempFilePaths[0]
//
this.uploadFilePromise(tempFilePath)
}
})
},
uploadFilePromise(url) {
let token = uni.getStorageSync('token') || ''
let a = uni.uploadFile({
url: this.uploadUrl, //
filePath: url,
name: 'file',
header: {
'token': `${token}`, //token
},
success: (e) => {
let res = JSON.parse(e.data.replace(/\ufeff/g, "") || "{}")
console.log('上传成功2', res)
if (res.code == 1) {
this.upload_type = 2
this.formData.head_img = res.data.url
// this.editHeadimg = res.data.path
// this.uploadHeadimg = res.data.url
} else {
uni.showToast({
title: res.msg,
icon: 'none'
})
}
},
});
},
//
changePickerSex(e) {
console.log('监听选择', e)
this.formData.gender = e.value
this.formData.gender_str = e.text
this.picker_show_sex = false
},
//
//+30
getBirthday() {
let date = new Date();
let year = date.getFullYear();
let month = date.getMonth() + 1;
let day = date.getDate();
let year_30 = year - 30;
let month_30 = month;
let day_30 = day;
if (month_30 == 2 && day_30 > 28) {
month_30 = 3;
day_30 = 1;
}
if (month_30 == 4 && day_30 > 30) {
month_30 = 5;
day_30 = 1;
}
if (month_30 == 6 && day_30 > 30) {
month_30 = 7;
day_30 = 1;
}
if (month_30 == 9 && day_30 > 30) {
month_30 = 10;
day_30 = 1;
}
if (month_30 == 11 && day_30 > 30) {
month_30 = 12;
day_30 = 1;
}
if (month_30 > 12) {
month_30 = month_30 - 12;
year_30 = year_30 + 1;
}
let minDate = year_30 + "-" + month_30 + "-" + day_30
let maxDate = year + "-" + month + "-" + day
this.minDate = minDate
this.maxDate = maxDate
},
//
changePickerBirthday(e) {
console.log('监听生日选择', e)
this.formData.birthday = e.result
this.picker_show_birthday = false
},
//
async submit() {
let data = {...this.formData}
if(!data.head_img){
uni.showToast({
title: '请上传头像',
icon: 'none'
})
return
}
if(!data.name){
uni.showToast({
title: '请填写',
icon: 'none'
})
return
}
if(!data.gender){
uni.showToast({
title: '请选择性别',
icon: 'none'
})
return
}
if(!data.birthday){
uni.showToast({
title: '请选择生日',
icon: 'none'
})
return
}
if(!data.email){
uni.showToast({
title: '请填写邮箱',
icon: 'none'
})
return
}
if(!data.phone){
uni.showToast({
title: '请填写手机',
icon: 'none'
})
return
}
let res = await apiRoute.editPersonnelInfo(data)
if(res.code != 1){
uni.showToast({
title: res.msg,
icon: 'none'
})
return
}
uni.showToast({
title: res.msg,
icon: 'success'
})
//1s
setTimeout(() => {
this.getUserInfo()
}, 1000)
},
}
}
</script>
<style lang="less" scoped>
.main_box{
background: #292929 ;
}
//
.navbar_section{
display: flex;
justify-content: center;
align-items: center;
background: #29d3b4;
.title{
padding: 40rpx 0rpx;
/* 小程序端样式 */
// #ifdef MP-WEIXIN
padding: 80rpx 0rpx;
// #endif
font-size: 30rpx;
color: #315d55;
}
}
.main_section{
min-height: 100vh;
background: #292929 100%;
padding: 0 0rpx;
padding-top: 32rpx;
padding-bottom: 150rpx;
font-size: 28rpx;
color: #fff;
display: flex;
flex-direction: column;
gap: 20rpx;
.section{
background-color: #434544;
.item{
padding: 20rpx 40rpx;
display: flex;
justify-content: space-between;
align-items: center;
.pic{
width: 82rpx;
height: 82rpx;
border-radius: 50%;
}
.btn{}
.title{
min-width: 100rpx;
display: flex;
align-items: center;
font-size: 26rpx;
color: #D7D7D7;
.required{
margin-left: 10rpx;
color: red;
}
}
.input{
display: flex;
justify-content: flex-end;
input{
text-align: right;
}
.dept{
width: 50%;
}
.disabled{
color: #808080;
//
cursor: not-allowed;
}
}
}
}
.submet_btn{
margin: 0 auto;
margin-top: 40rpx;
border: 2px solid #25a18b;
color: #25a18b;
width: 80%;
height: 80rpx;
font-size: 30rpx;
display: flex;
justify-content: center;
align-items: center;
}
}
</style>

292
uniapp/pages/market/my/my_data.vue

@ -1,292 +0,0 @@
<!--我的数据-->
<template>
<view class="main_box">
<!--自定义导航栏-->
<view class="navbar_section">
<view class="navbar_content">
<view class="back_btn" @click="goBack">
<fui-icon name="arrowleft" size="32" color="#fff"></fui-icon>
</view>
<view class="title">我的数据</view>
<view class="placeholder"></view>
</view>
</view>
<view class="content_section">
<!-- 数据概览卡片 -->
<view class="overview_cards">
<view class="card_item">
<view class="card_icon">
<fui-icon name="star" size="40" color="#29D3B4"></fui-icon>
</view>
<view class="card_content">
<view class="card_title">总客户数</view>
<view class="card_value">--</view>
</view>
</view>
<view class="card_item">
<view class="card_icon">
<fui-icon name="check" size="40" color="#29D3B4"></fui-icon>
</view>
<view class="card_content">
<view class="card_title">已签客户</view>
<view class="card_value">--</view>
</view>
</view>
<view class="card_item">
<view class="card_icon">
<fui-icon name="wallet" size="40" color="#29D3B4"></fui-icon>
</view>
<view class="card_content">
<view class="card_title">销售业绩</view>
<view class="card_value">--</view>
</view>
</view>
<view class="card_item">
<view class="card_icon">
<fui-icon name="calendar" size="40" color="#29D3B4"></fui-icon>
</view>
<view class="card_content">
<view class="card_title">本月任务</view>
<view class="card_value">--</view>
</view>
</view>
</view>
<!-- 功能按钮区域 -->
<view class="function_section">
<view class="section_title">数据统计</view>
<view class="function_grid">
<view class="function_item">
<view class="function_icon">
<fui-icon name="barchart" size="32" color="#29D3B4"></fui-icon>
</view>
<view class="function_text">客户统计</view>
</view>
<view class="function_item">
<view class="function_icon">
<fui-icon name="piechart" size="32" color="#29D3B4"></fui-icon>
</view>
<view class="function_text">业绩统计</view>
</view>
<view class="function_item">
<view class="function_icon">
<fui-icon name="linechart" size="32" color="#29D3B4"></fui-icon>
</view>
<view class="function_text">趋势分析</view>
</view>
<view class="function_item">
<view class="function_icon">
<fui-icon name="list" size="32" color="#29D3B4"></fui-icon>
</view>
<view class="function_text">详细报表</view>
</view>
</view>
</view>
<!-- 提示信息 -->
<view class="tips_section">
<view class="tips_title">功能说明</view>
<view class="tips_content">
这里将显示您个人的各项数据统计包括客户数量销售业绩任务完成情况等
具体功能待后续开发实现
</view>
</view>
</view>
</view>
</template>
<script>
import fuiIcon from "@/components/firstui/fui-icon/fui-icon.vue"
export default {
components: {
fuiIcon,
},
data() {
return {
}
},
onLoad() {
},
methods: {
//
goBack() {
uni.navigateBack()
},
}
}
</script>
<style lang="less" scoped>
.main_box {
background: #f5f5f5;
min-height: 100vh;
}
//
.navbar_section {
background: #29D3B4;
padding-top: 20rpx;
//
// #ifdef MP-WEIXIN
padding-top: 90rpx;
// #endif
.navbar_content {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx 24rpx 40rpx 24rpx;
.back_btn {
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
}
.title {
font-size: 32rpx;
color: #fff;
font-weight: 500;
}
.placeholder {
width: 60rpx;
}
}
}
//
.content_section {
padding: 40rpx 24rpx;
//
.overview_cards {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 24rpx;
margin-bottom: 40rpx;
.card_item {
background: #fff;
border-radius: 16rpx;
padding: 32rpx 24rpx;
display: flex;
align-items: center;
gap: 20rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
.card_icon {
width: 64rpx;
height: 64rpx;
background: rgba(41, 211, 180, 0.1);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.card_content {
flex: 1;
.card_title {
font-size: 24rpx;
color: #999;
margin-bottom: 8rpx;
}
.card_value {
font-size: 32rpx;
color: #333;
font-weight: 600;
}
}
}
}
//
.function_section {
margin-bottom: 40rpx;
.section_title {
font-size: 28rpx;
color: #333;
font-weight: 600;
margin-bottom: 24rpx;
}
.function_grid {
background: #fff;
border-radius: 16rpx;
padding: 32rpx 24rpx;
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 32rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
.function_item {
display: flex;
flex-direction: column;
align-items: center;
padding: 24rpx 16rpx;
border-radius: 12rpx;
transition: all 0.3s ease;
&:active {
background-color: #f5f5f5;
transform: scale(0.95);
}
.function_icon {
width: 64rpx;
height: 64rpx;
background: rgba(41, 211, 180, 0.1);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 16rpx;
}
.function_text {
font-size: 24rpx;
color: #333;
}
}
}
}
//
.tips_section {
background: #fff;
border-radius: 16rpx;
padding: 32rpx 24rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
.tips_title {
font-size: 28rpx;
color: #333;
font-weight: 600;
margin-bottom: 16rpx;
}
.tips_content {
font-size: 24rpx;
color: #666;
line-height: 1.6;
}
}
}
</style>

105
uniapp/pages/market/my/set_up.vue

@ -1,105 +0,0 @@
<!--设置页-->
<template>
<view class="assemble">
<view style="height: 30rpx;"></view>
<view class="option" @click="update_pass()">修改密码</view>
<view class="option" @click="privacy_agreement(1)">用户协议</view>
<view class="option" @click="privacy_agreement(2)">隐私策略</view>
<view class="option" @click="clearCache()">清空缓存</view>
<view style="width:90%;margin: 60rpx auto;">
<fui-button background="#29d3b4" @click="loginOut()">退出账号</fui-button>
</view>
</view>
</template>
<script>
export default {
data() {
return {
}
},
methods: {
//退
loginOut(){
this.$util.loginOut()
},
privacy_agreement(type){
uni.navigateTo({
url: '/pages-common/privacy_agreement?type='+type
})
},
update_pass(){
uni.navigateTo({
url: '/pages-market/my/update_pass'
})
},
//
clearCache() {
uni.showModal({
title: '清空缓存',
content: '确定要清空所有字典缓存吗?',
success: (res) => {
if (res.confirm) {
this.performClearCache();
}
}
});
},
//
performClearCache() {
try {
//
const storageInfo = uni.getStorageInfoSync();
const keys = storageInfo.keys;
// dict_
let clearCount = 0;
keys.forEach(key => {
if (key.startsWith('dict_')) {
uni.removeStorageSync(key);
clearCount++;
}
});
//
uni.showToast({
title: `已清空${clearCount}个字典缓存`,
icon: 'success',
duration: 2000
});
console.log(`清空缓存完成,共清理${clearCount}个dict_开头的缓存项`);
} catch (error) {
console.error('清空缓存失败:', error);
uni.showToast({
title: '清空缓存失败',
icon: 'none',
duration: 2000
});
}
}
}
}
</script>
<style lang="less" scoped>
.assemble{
width: 100%;
height: 100vh;
background: #333333;
}
.option{
margin-bottom: 20rpx;
background: #404045;
width: 100%;
font-size: 28rpx;
color: #fff;
line-height: 56rpx;
padding: 20rpx 0 20rpx 100rpx;
}
</style>

275
uniapp/pages/market/my/signed_client_list.vue

@ -1,275 +0,0 @@
<!--已签客户-列表-->
<template>
<view class="main_box">
<!--自定义导航栏-->
<!-- <view class="navbar_section">-->
<!-- <view class="title">班级详情</view>-->
<!-- </view>-->
<view class="main_section">
<!-- 班级成员列表-->
<scroll-view
class="section_4"
scroll-y="true"
:lower-threshold="lowerThreshold"
@scrolltolower="loadMoreData"
style="height: 83vh;"
>
<view class="ul">
<view class="li"
v-for="(v,k) in tableList"
:key="k"
@click="openViewStudentInfo(v)">
<view class="left">
<view class="box_1">
<image class="pic"
v-if="v.header" :src="$util.img(v.header)"></image>
<image v-else class="pic" :src="$util.img('/uniapp_src/static/images/index/myk.png')"></image>
<!-- <view class="tag_box">-->
<!-- 即将到期-->
<!-- </view>-->
</view>
<view class="box_2">
<view class="name">{{v.name}}</view>
<view class="date">课程截止时间{{v.end_time}}</view>
</view>
</view>
<view class="right">
<view class="item">
<view>{{v.have_study_time}}</view>
<view>已上课时</view>
</view>
<view class="item">
<view>{{v.end_study_time ? v.end_study_time:0}}</view>
<view>剩余课时</view>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
<!-- 底部导航-->
<!-- <AQTabber/>-->
</view>
</template>
<script>
import marketApi from '@/api/market.js';
import AQTabber from "@/components/AQ/AQTabber.vue"
export default {
components: {
AQTabber,
},
data() {
return {
loading:false,//
lowerThreshold: 100,//
isReachedBottom: false,//|true=|false=
//
filteredData:{
page:1,//
limit:10,//
total:10,//
name: '',//
},
tableList:[],//
}
},
onLoad() {
},
onShow() {
this.init();
},
methods: {
//
async init(){
await this.getList()//
},
//()
loadMoreData() {
//
if (!this.isReachedBottom) {
this.isReachedBottom = true;//
this.getList();
}
},
//
async resetFilteredData() {
this.isReachedBottom = false; // 便
this.filteredData.page = 1//
this.filteredData.limit = 10//
this.filteredData.total = 10//
},
//
async getList(){
this.loading = true
let data = {...this.filteredData}
//
if ((this.filteredData.page - 1) * this.filteredData.limit >= this.filteredData.total) {
this.loading = false
uni.showToast({
title: '暂无更多',
icon: 'none'
})
return
}
if(data.page == 1){
this.tableList = []
}
let res = await marketApi.signClient(data)
this.loading = false
this.isReachedBottom = false;
if (res.code != 1){
uni.showToast({
title: res.msg,
icon: 'none'
})
return
}
this.tableList = this.tableList.concat(res.data.data); // 使 concat
console.log('列表',this.tableList)
this.filteredData.total = res.data.total
this.filteredData.page++
},
//
openViewStudentInfo(item){
let students_id= item.id
uni.navigateTo({
url: `/pages-coach/coach/student/info?students_id=${students_id}`
})
},
}
}
</script>
<style lang="less" scoped>
.main_box{
background: #292929 ;
}
//
.navbar_section{
display: flex;
justify-content: center;
align-items: center;
background: #292929;
.title{
padding: 40rpx 0rpx;
/* 小程序端样式 */
// #ifdef MP-WEIXIN
padding: 80rpx 0rpx;
// #endif
font-size: 30rpx;
color: #fff;
}
}
.main_section{
min-height: 95vh;
background: #292929 100%;
padding: 0 24rpx;
padding-top: 40rpx;
padding-bottom: 150rpx;
font-size: 24rpx;
color: #FFFFFF;
//
.section_4{
.ul{
display: flex;
flex-direction: column;
gap: 10rpx;
.li{
padding: 20rpx 0;
padding-bottom: 40rpx;
border-bottom: 2px solid #D7D7D7;
display: flex;
justify-content: space-between;
.left{
display: flex;
align-items: center;
gap: 30rpx;
.box_1{
padding-left: 20rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
.pic{
width: 84rpx;
height: 84rpx;
border-radius: 50%;
}
.tag_box{
position: absolute;
bottom: -30rpx;
width: 120rpx;
height: 38rpx;
background-color: #F59A23;
border-radius: 4rpx;
line-height: 35rpx;
text-align: center;
font-size: 20rpx;
}
}
.box_2{
display: flex;
flex-direction: column;
gap: 20rpx;
.name{
font-size: 28rpx;
}
.date{
font-size: 24rpx;
}
}
}
.right{
display: flex;
align-items: center;
gap: 14rpx;
.item{
border: 1px solid #00E5BB;
border-radius: 10rpx;
width: 102rpx;
display: flex;
flex-direction: column;
view{
text-align: center;
height: 50rpx;
line-height: 50rpx;
}
view:nth-child(1){
font-size: 32rpx;
background-color: #fff;
color: #00e5bb;
}
view:nth-child(2){
font-size: 20rpx;
background-color: #00e5bb;
}
}
}
}
}
}
}
</style>

225
uniapp/pages/market/my/update_pass.vue

@ -1,225 +0,0 @@
<!--修改密码-->
<template>
<view>
<view class="title">
<view :class="{'green-text': tset_style === 1}">1.验证旧密码</view>
<view :class="{'green-text': tset_style === 2}">2.设置新密码</view>
</view>
<view :style="{'background-color':'#fff','width':'100%','height':'100vh' }">
<view style="width: 95%;height: 30rpx;"></view>
<view v-if="tset_style == 1">
<view class="describe">
为保障您的账号安全修改密码前请填写原密码
</view>
<view style="width: 95%;margin:30rpx auto;">
<fui-input borderTop placeholder="请输入原登录密码" v-model="old_password"
backgroundColor="#f2f2f2"></fui-input>
</view>
</view>
<view v-if="tset_style == 2">
<view style="width: 95%;margin:30rpx auto;">
<fui-input borderTop placeholder="请设置6-20位新的登录密码" v-model="formData.new_password" @input="input"
backgroundColor="#f2f2f2"></fui-input>
</view>
<view style="width: 95%;margin: auto;">
<fui-input borderTop :padding="['20rpx','32rpx']" v-model="formData.new_password_2" placeholder="请再次输入新的登录密码" @input="input"
backgroundColor="#f2f2f2">
</fui-input>
</view>
</view>
<view style="width: 95%;margin:60rpx auto;">
<view class="btn_box">
<fui-button
background="#465cff"
radius="5rpx"
@click="nextStep(1)"
v-if="tset_style != 1">上一步
</fui-button>
<fui-button background="#00be8c" radius="5rpx" @click="nextStep(2)" v-if="tset_style == 1">下一步</fui-button>
<fui-button background="#00be8c" radius="5rpx" @click="submit" v-if="tset_style == 2">提交</fui-button>
</view>
<view style="width: 95%;margin:60rpx auto;">
<fui-button background="#fff" radius="5rpx" @click="forgot" color="#999999" v-if="tset_style == 1">忘记原密码</fui-button>
</view>
</view>
</view>
</view>
</template>
<script>
import apiRoute from '@/api/apiRoute.js';
export default {
data() {
return {
code: '',
user: '',
tset_style: 1,//tab|1=,2=
old_password:'',//
formData:{
phone:'',//
new_password:'',//
new_password_2:'',//
key_value:'',//key_value
},
}
},
onLoad() {},
onShow() {
this.init()//
},
methods: {
//
async init(){
await this.getUserInfo()
},
//
async getUserInfo() {
let res = await apiRoute.getPersonnelInfo({})
if (res.code != 1) {
uni.showToast({
title: res.msg,
icon: 'none'
})
return
}
this.formData.phone = res.data.phone//
},
///
async nextStep(tset_style){
//tset_style|1=,2=
if(tset_style == 2){
if(!this.old_password){
uni.showToast({
title: '请输入原登录密码',
icon: 'none'
})
return
}
//
let params = {
old_password: this.old_password
}
//
let res = await apiRoute.common_personnelCheckOldPwd(params)
if(!res.code){
uni.showToast({
title: res.msg,
icon: 'none'
})
return
}
this.formData.key_value = res.data.key_value
}
this.tset_style = Number(tset_style)
},
//
forgot() {
uni.navigateTo({
url: '/pages/student/login/forgot'
})
},
//
async submit() {
//
if (!this.formData.new_password) {
uni.showToast({
title: '请输入新密码',
icon: 'none'
})
return
}
if (this.formData.new_password.length < 6 || this.formData.new_password.length > 20) {
uni.showToast({
title: '新密码长度为6-20位',
icon: 'none'
})
return
}
if (!this.formData.new_password) {
uni.showToast({
title: '请输入新密码',
icon: 'none'
})
return
}
//
if (this.formData.new_password != this.formData.new_password_2) {
uni.showToast({
title: '两次密码不一致',
icon: 'none'
})
return
}
let res = await apiRoute.common_personnelEdidPassword(this.formData)
if(!res.code){
uni.showToast({
title: res.msg,
icon: 'none'
})
return
}
uni.showToast({
title: res.msg,
icon: 'success'
})
//1s
setTimeout(() => {
uni.navigateBack()
}, 1000)
},
}
}
</script>
<style lang="less" scoped>
page {
font-weight: normal;
}
.fui-section__title {
margin-left: 32rpx;
}
.fui-left__icon {
padding-right: 24rpx;
}
.title {
display: flex;
justify-content: space-around;
align-items: center;
height: 100rpx;
width: 100%;
background-color: #fff;
font-size: 26rpx;
border: 4rpx #f5f5f5 solid;
}
.green-text{
color: #36d6b9;
}
.describe{
color: #999999;
padding-left: 30rpx;
}
.btn_box{
display: flex;
flex-direction: column;
gap: 40rpx;
}
</style>

272
uniapp/pages/market/reimbursement/add.vue

@ -1,272 +0,0 @@
<template>
<view class="reim-add-page">
<view class="header-bar">
<view class="title">{{ pageTitle }}</view>
</view>
<view class="form-box">
<!-- 金额 -->
<view class="form-row">
<view class="label">报销金额</view>
<input class="input-amount" type="number" v-model="form.amount" placeholder="0.00"
:disabled="disabled" />
</view>
<!-- 描述 -->
<view class="form-row-top">
<view class="label">报销描述</view>
<textarea class="textarea" v-model="form.description" placeholder="请输入报销事由" :disabled="disabled" />
</view>
<!-- 附件 -->
<view class="form-row">
<view class="label">发票/收据</view>
<view class="file-upload-wrapper">
<view v-if="form.receipt_url" class="preview-box" @click="previewImage">
<image v-if="isImage(form.receipt_url)" :src="form.receipt_url" class="preview-img"
mode="aspectFit" />
<view v-else class="file-link">{{ getFileName(form.receipt_url) }}</view>
</view>
<button class="upload-btn" @click="chooseFile" :disabled="disabled">
{{ form.receipt_url ? '重新选择' : '选择附件' }}
</button>
</view>
</view>
</view>
<view class="save-btn-box" v-if="!disabled">
<fui-button background="#434544" color="#24BA9F" borderColor="#24BA9F" @click="submit">提交</fui-button>
</view>
</view>
</template>
<script>
import apiRoute from '@/api/apiRoute.js';
import {
Api_url
} from "@/common/config.js";
export default {
data() {
return {
uploadUrl: `${Api_url}/uploadImage`,
pageTitle: '新增报销',
disabled: false,
form: {
id: null,
amount: '',
description: '',
receipt_url: '',
}
}
},
onLoad(options) {
if (options.id) {
this.form.id = options.id;
this.fetchDetail(options.id);
}
},
methods: {
async fetchDetail(id) {
uni.showLoading({
title: '加载中...'
});
let res = await apiRoute.reimbursement_info({id:id})
let mockData = res.data
if (mockData.status !== 'pending') {
this.disabled = true;
this.pageTitle = '查看报销';
} else {
this.pageTitle = '编辑报销';
}
this.form.amount = mockData.amount;
this.form.description = mockData.description;
this.form.receipt_url = mockData.receipt_url;
uni.hideLoading();
},
chooseFile() {
uni.chooseImage({
count: 1,
success: (res) => {
const tempFilePath = res.tempFilePaths[0]
//
this.uploadFilePromise(tempFilePath)
}
})
},
uploadFilePromise(url) {
let token = uni.getStorageSync('token') || ''
let a = uni.uploadFile({
url: this.uploadUrl, //
filePath: url,
name: 'file',
header: {
'token': `${token}`, //token
},
success: (e) => {
let res = JSON.parse(e.data.replace(/\ufeff/g, "") || "{}")
if (res.code == 1) {
this.form.receipt_url = res.data.url
} else {
uni.showToast({
title: res.msg,
icon: 'none'
})
}
},
});
},
isImage(url) {
if (!url) return false;
return /\.(jpg|jpeg|png|gif|bmp)$/i.test(url);
},
getFileName(path) {
if (!path) return '';
return path.split('/').pop();
},
previewImage() {
if (this.form.receipt_url && this.isImage(this.form.receipt_url)) {
uni.previewImage({
urls: [this.form.receipt_url]
});
}
},
submit() {
if (!this.form.amount || !this.form.description) {
uni.showToast({
title: '请填写完整',
icon: 'none'
});
return;
}
let res = apiRoute.reimbursement_add(this.form)
if (res['code'] == 1) {
uni.showToast({
title: '提交成功',
icon: 'success'
});
}
setTimeout(() => {
uni.navigateBack();
}, 1000);
}
}
}
</script>
<style lang="less" scoped>
.reim-add-page {
min-height: 100vh;
background: #292929;
padding-bottom: 120rpx;
}
.header-bar {
padding: 32rpx;
.title {
font-size: 44rpx;
color: #fff;
font-weight: bold;
}
}
.form-box {
margin: 0 32rpx;
background: #434544;
border-radius: 18rpx;
padding: 0 24rpx;
}
.form-row,
.form-row-top {
display: flex;
padding: 32rpx 0;
border-bottom: 1rpx solid #3a3a3a;
&:last-of-type {
border-bottom: none;
}
}
.form-row {
align-items: center;
}
.form-row-top {
align-items: flex-start;
}
.label {
color: #aaa;
font-size: 28rpx;
width: 180rpx;
flex-shrink: 0;
}
.input-amount {
flex: 1;
color: #fff;
font-size: 28rpx;
text-align: right;
}
.textarea {
flex: 1;
height: 180rpx;
color: #fff;
font-size: 28rpx;
background-color: transparent;
padding: 0;
width: 100%;
}
.file-upload-wrapper {
flex: 1;
display: flex;
justify-content: flex-end;
align-items: center;
gap: 20rpx;
}
.upload-btn {
background: #24BA9F;
color: #fff;
border: none;
border-radius: 8rpx;
padding: 0 32rpx;
line-height: 64rpx;
height: 64rpx;
font-size: 26rpx;
margin: 0;
}
.preview-box {
.preview-img {
width: 120rpx;
height: 120rpx;
border-radius: 8rpx;
background: #222;
border: 1rpx solid #333;
}
.file-link {
color: #24BA9F;
font-size: 26rpx;
word-break: break-all;
}
}
.save-btn-box {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 100;
background: #292929;
padding: 20rpx 40rpx 40rpx 40rpx;
box-shadow: 0 -2rpx 8rpx rgba(0, 0, 0, 0.12);
}
</style>

136
uniapp/pages/market/reimbursement/detail.vue

@ -1,136 +0,0 @@
<template>
<view class="reim-detail-page">
<view class="header-bar">
<view class="title">报销详情</view>
</view>
<view class="detail-box">
<view class="row">
<view class="label">报销金额</view>
<view class="value">{{ detail.amount }}</view>
</view>
<view class="row">
<view class="label">报销描述</view>
<view class="value">{{ detail.description }}</view>
</view>
<view class="row">
<view class="label">发票/收据</view>
<view class="value">
<image v-if="detail.receipt_url && isImage(detail.receipt_url)" :src="detail.receipt_url" class="receipt-img" mode="aspectFit" />
<view v-else-if="detail.receipt_url" class="file-link">{{ detail.receipt_url }}</view>
<text v-else>无附件</text>
</view>
</view>
<view class="row">
<view class="label">状态</view>
<view :class="['value', 'status-' + detail.status]">{{ statusMap[detail.status] || detail.status }}</view>
</view>
<view class="row">
<view class="label">创建时间</view>
<view class="value">{{ detail.created_at }}</view>
</view>
<view class="row">
<view class="label">修改时间</view>
<view class="value">{{ detail.updated_at }}</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
detail: {},
statusMap: {
pending: '待审批',
approved: '已批准',
rejected: '已拒绝',
},
}
},
onLoad(options) {
if (options.id) {
this.fetchDetail(options.id);
} else {
uni.showToast({ title: '缺少报销ID', icon: 'none' });
uni.navigateBack();
}
},
methods: {
fetchDetail(id) {
//
uni.showLoading({ title: '加载中...' });
setTimeout(() => {
const mockData = {
id: id,
amount: 300.00,
description: '办公用品采购(已批准)',
receipt_url: 'https://cdn.uviewui.com/uview/swiper/1.jpg',
status: 'approved',
created_at: '2024-06-02 09:30',
updated_at: '2024-06-03 12:00',
};
this.detail = mockData;
uni.hideLoading();
}, 500);
},
isImage(url) {
if (!url) return false;
return /\.(jpg|jpeg|png|gif|bmp)$/i.test(url)
}
}
}
</script>
<style lang="less" scoped>
.reim-detail-page {
min-height: 100vh;
background: #292929;
}
.header-bar {
padding: 32rpx 32rpx 0 32rpx;
.title {
font-size: 36rpx;
color: #24BA9F;
font-weight: bold;
}
}
.detail-box {
margin: 32rpx;
background: #434544;
border-radius: 18rpx;
padding: 32rpx 24rpx 24rpx 24rpx;
}
.row {
display: flex;
align-items: flex-start;
margin-bottom: 32rpx;
.label {
color: #aaa;
font-size: 28rpx;
width: 180rpx;
flex-shrink: 0;
margin-top: 10rpx;
}
.value {
color: #fff;
font-size: 28rpx;
word-break: break-all;
}
.status-pending { color: #f0ad4e; }
.status-approved { color: #24BA9F; }
.status-rejected { color: #e74c3c; }
.receipt-img {
width: 160rpx;
height: 160rpx;
border-radius: 8rpx;
background: #222;
border: 1rpx solid #333;
}
.file-link {
color: #24BA9F;
font-size: 26rpx;
word-break: break-all;
}
}
</style>

144
uniapp/pages/market/reimbursement/list.vue

@ -1,144 +0,0 @@
<template>
<view class="reim-list-page">
<view class="header-bar">
<view class="title">报销列表</view>
<fui-button
background="transparent"
color="#24BA9F"
borderColor="#24BA9F"
width="180rpx"
height="64rpx"
radius="32rpx"
@click="goAdd">
新增报销
</fui-button>
</view>
<view v-if="list.length === 0" class="empty-tip">暂无报销记录</view>
<view v-for="item in list" :key="item.id" class="reim-card" @click="goDetail(item)">
<view class="row">
<view class="label">金额</view>
<view class="value">{{ item.amount }}</view>
</view>
<view class="row">
<view class="label">描述</view>
<view class="value">{{ item.description }}</view>
</view>
<view class="row">
<view class="label">发票/收据</view>
<view class="value">
<image v-if="item.receipt_url" :src="item.receipt_url" class="receipt-img" mode="aspectFit" />
<text v-else>无附件</text>
</view>
</view>
<view class="row">
<view class="label">状态</view>
<view :class="['value', 'status-' + item.status]">{{ statusMap[item.status] || item.status }}</view>
</view>
<view class="row">
<view class="label">创建时间</view>
<view class="value">{{ item.created_at }}</view>
</view>
</view>
</view>
</template>
<script>
import apiRoute from '@/api/apiRoute.js';
export default {
data() {
return {
list: [],
statusMap: {
pending: '待审批',
approved: '已批准',
rejected: '已拒绝',
},
}
},
async onShow() {
await this.reimbursementList();
},
methods: {
async reimbursementList(){
let res = await apiRoute.reimbursement_list({})
this.list = res.data;
},
goAdd() {
uni.navigateTo({
url: '/pages-market/reimbursement/add'
});
},
goDetail(item) {
//
if (item.status === 'pending') {
this.$navigateToPage(`/pages-market/reimbursement/add`, {
id: item.id
});
} else {
this.$navigateToPage(`/pages-market/reimbursement/detail`, {
id: item.id
});
}
}
}
}
</script>
<style lang="less" scoped>
.reim-list-page {
min-height: 100vh;
background: #292929;
padding-bottom: 120rpx;
}
.header-bar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 32rpx 32rpx 0 32rpx;
.title {
font-size: 36rpx;
color: #fff;
font-weight: bold;
}
}
.empty-tip {
color: #888;
text-align: center;
margin: 80rpx 0;
}
.reim-card {
background: #434544;
border-radius: 18rpx;
margin: 32rpx;
margin-bottom: 0;
padding: 32rpx 24rpx 24rpx 24rpx;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.08);
.row {
display: flex;
align-items: center;
margin-bottom: 18rpx;
.label {
color: #aaa;
font-size: 26rpx;
width: 140rpx;
flex-shrink: 0;
}
.value {
color: #fff;
font-size: 28rpx;
word-break: break-all;
}
.status-pending { color: #f0ad4e; }
.status-approved { color: #24BA9F; }
.status-rejected { color: #e74c3c; }
.receipt-img {
width: 120rpx;
height: 120rpx;
border-radius: 8rpx;
background: #222;
border: 1rpx solid #333;
}
}
}
</style>
Loading…
Cancel
Save