From 5d3631f79e8bb77f4ffe6c22cff895b6b4ca2272 Mon Sep 17 00:00:00 2001
From: lion <120344285@qq.com>
Date: Tue, 23 Sep 2025 16:36:52 +0800
Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
common/config.js | 7 +-
common/http.api.js | 6 +
package_sub/pages/addQuality/addQuality.vue | 460 ++++++++++++--------
package_sub/pages/index/index.vue | 2 +-
package_sub/pages/quality/quality.vue | 149 ++++++-
5 files changed, 427 insertions(+), 197 deletions(-)
diff --git a/common/config.js b/common/config.js
index 3348a4d..140a0ce 100644
--- a/common/config.js
+++ b/common/config.js
@@ -1,13 +1,14 @@
const mode = process.env.NODE_ENV;
// const mode = 'development';
-let ROOTPATH = ''; //域名
+let ROOTPATH = 'https://sstt.ali251.langye.net'; //域名
+// let ROOTPATH = 'https://sstt.115.langye.net'; //域名
switch (mode) {
case 'development':
// ROOTPATH = "https://sstt.ali251.langye.net"
- ROOTPATH = "https://sstt.115.langye.net"
+ ROOTPATH = ROOTPATH
break;
case 'production':
- ROOTPATH = "https://sstt.115.langye.net"
+ ROOTPATH = ROOTPATH
break;
default:
throw new Error('未配置环境');
diff --git a/common/http.api.js b/common/http.api.js
index dedef51..84fc255 100644
--- a/common/http.api.js
+++ b/common/http.api.js
@@ -25,6 +25,8 @@ let apiAdmin = {
me: "/api/admin/auth/me",
scheduleList: "/api/admin/schedule/schedule-index",
customerList: "/api/admin/customer/get-list",
+ customerInfo:'/api/admin/customer/get-info',
+ customerSave:'/api/admin/customer/save',
saveCallback: "/api/admin/schedule-list-callbacks/save",
saveCheck: "/api/admin/schedule-list-checks/save",
callbackList: "/api/admin/schedule-list-callbacks/get-list",
@@ -67,6 +69,8 @@ const install = (Vue, vm) => {
let adminMe = (data = {}) => vm.$u.post(apiAdmin.me, data);
let adminScheduleList = (data = {}) => vm.$u.get(apiAdmin.scheduleList, data);
let adminCustomerList = (data = {}) => vm.$u.get(apiAdmin.customerList, data);
+ let adminCustomerInfo = (params = {}) => vm.$u.get(apiAdmin.customerInfo+`/${params.id}`, params);
+ let adminCustomerSave = (data = {}) => vm.$u.post(apiAdmin.customerSave, data);
let adminSaveCallback = (data = {}) => vm.$u.post(apiAdmin.saveCallback, data);
let adminSaveCheck = (data = {}) => vm.$u.post(apiAdmin.saveCheck, data);
let adminCallbackList = (data = {}) => vm.$u.get(apiAdmin.callbackList, data);
@@ -106,6 +110,8 @@ const install = (Vue, vm) => {
adminMe,
adminScheduleList,
adminCustomerList,
+ adminCustomerInfo,
+ adminCustomerSave,
adminSaveCallback,
adminSaveCheck,
adminCallbackList,
diff --git a/package_sub/pages/addQuality/addQuality.vue b/package_sub/pages/addQuality/addQuality.vue
index 0a9b403..7d24bad 100644
--- a/package_sub/pages/addQuality/addQuality.vue
+++ b/package_sub/pages/addQuality/addQuality.vue
@@ -4,16 +4,48 @@
-
-
-
-
-
-
-
-
+
+
-
+
+
+
+
+
+
+
+
+
+
+ 正常
+ 死亡
+
+
+
+
+
+
+ {{ defaultCustomerAddress.address || '' }}
+
+ 获取位置
+
+
+
+
+ 获取位置
+
+
+
+
+
+
+ 更新用户信息
+
+
+
+
+
+
checkboxChange(e)">
@@ -27,12 +59,12 @@
-
-
-
-
-
-
+
+
+
+
+
+
{
if (this.vuex_sign_image || value) {
@@ -263,23 +301,91 @@
deep: true, // 深度监听
immediate: true // 初始化时立即执行一次
}
- },
- onLoad(option) {
- this.form.customer_id = option.customer_id;
- this.id = option.id;
- this.type = option.id ? 'edit' : 'add';
- if (this.type === 'edit') {
- this.getDetail()
- }else{
- this.form.date = moment().format('YYYY-MM-DD')
- this.form.customer_name = option.customer_name?option.customer_name:''
- }
- },
- onReady() {
- this.load();
- this.$refs.uForm.setRules(this.rules);
},
- methods: {
+ onLoad(option) {
+ this.form.customer_id = option.customer_id;
+ this.id = option.id;
+ this.type = option.id ? 'edit' : 'add';
+ if (this.type === 'edit') {
+ this.getDetail()
+ }else{
+ this.form.date = moment().format('YYYY-MM-DD')
+ this.form.customer_name = option.customer_name?option.customer_name:''
+ }
+ if (this.form.customer_id) {
+ this.getCustomerInfo()
+ }
+ },
+ onReady() {
+ this.load();
+ this.$refs.uForm.setRules(this.rules);
+ },
+ computed: {
+ defaultCustomerAddress(){
+ const idx = this.editCustomer.customer_address.findIndex(i => Number(i.default) === 1)
+ return idx > -1 ? this.editCustomer.customer_address[idx] : null
+ }
+ },
+ methods: {
+ async getCustomerInfo(){
+ try{
+ const res = await this.$u.api.adminCustomerInfo({ id: this.form.customer_id })
+ this.customerInfo = res
+ // 初始化可编辑数据
+ this.editCustomer.phone = res.phone || ''
+ this.editCustomer.is_dead = Number(res.is_dead || 0)
+ this.editCustomer.customer_address = Array.isArray(res.customer_address) ? JSON.parse(JSON.stringify(res.customer_address)) : []
+ }catch(e){
+ console.error('adminCustomerInfo error', e)
+ }
+ },
+ defaultAddrIndex() {
+ return this.editCustomer.customer_address.findIndex(i => Number(i.default) === 1)
+ },
+ // 获取定位并更新默认地址(已有默认地址)
+ async updateDefaultCustomerAddress(){
+ try{
+ const res = await uni.getLocation({ type: 'gcj02', isHighAccuracy: true })
+ const location = Array.isArray(res) ? res[1] : res
+ if(!location) throw new Error('定位失败')
+ await new Promise((resolve, reject) => {
+ this.qqmapsdk.reverseGeocoder({
+ location: { latitude: location.latitude, longitude: location.longitude },
+ success: (r) => resolve(r),
+ fail: (err) => reject(err)
+ })
+ }).then(geo => {
+ const idx = this.defaultAddrIndex()
+ const addr = geo.result.address + (geo.result.formatted_addresses?.recommend || '')
+ // 如果已存在默认地址,先移除该条(避免携带旧的 id)
+ if (idx > -1) {
+ this.editCustomer.customer_address.splice(idx, 1)
+ }
+ // 插入新的默认地址,且不包含 id 字段
+ this.editCustomer.customer_address.push({ address: addr, lat: location.latitude, lng: location.longitude, default: 1 })
+ uni.showToast({ icon: 'none', title: '位置已更新' })
+ })
+ }catch(e){
+ uni.showToast({ icon: 'none', title: '定位失败' })
+ }
+ },
+ // 无默认地址时创建
+ async createDefaultCustomerAddress(){
+ await this.updateDefaultCustomerAddress()
+ },
+ saveCustomerInfo(){
+ const payload = {
+ ...this.customerInfo,
+ id: this.form.customer_id,
+ is_dead: this.editCustomer.is_dead,
+ phone: this.editCustomer.phone,
+ customer_address_list: this.editCustomer.customer_address
+ }
+ this.$u.api.adminCustomerSave(payload).then(()=>{
+ uni.showToast({ icon:'success', title:'已更新' })
+ this.getCustomerInfo()
+ })
+ },
showimg(url) {
if (url)
@@ -338,132 +444,132 @@
let arr = e.filter(i => i?.trim())
console.log("arr", e, arr)
// this.$set(item, 'score', arr)
- },
- blurInput(item){
- console.log("blur",item)
- if(!this.validateNumberRange(item.score,item.min,item.max)){
- uni.showToast({
- title: "请输入正确的分值",
- icon: "none"
- })
- item.score = 0
- }
- },
- // 计算总分
- calculateTotalScore(form) {
- // 处理 forms 不存在或非数组的情况
- if (!Array.isArray(this.form.forms)) {
- this.form.total_score = 0;
- return;
- }
- // 检查是否存在任何一个选项的 checked 为 true
- const hasCheckedOption = this.form.forms.some(item => {
- // 如果 item.options 存在且包含 checked 为 true 的项
- return Array.isArray(item.options) &&
- item.options.some(option => option.checked === true);
- });
-
- // 如果存在 checked 为 true 的选项,直接返回 0 分
- if (hasCheckedOption) {
- this.form.total_score = 0;
- this.form.forms.map(item=>item.score=0)
- return;
- }
- // 否则,正常累加所有项的分数
- const total = this.form.forms.reduce((sum, item) => {
- // 将 item.score 转换为数字,非数字默认 0
- const score = Number(item.score) || 0;
- return sum + score;
- }, 0);
-
- this.form.total_score = total;
- },
- // 判断是否为有效数子
- validateNumberRange(value, min, max) {
- // 步骤1:判断值是否为有效数字(处理字符串形式的数字)
- let num;
-
- // 如果已经是数字类型,直接使用
- if (typeof value === 'number') {
- num = value;
- }
- // 如果是字符串,尝试转换为数字
- else if (typeof value === 'string') {
- // 去除首尾空格
- const trimmed = value.trim();
-
- // 使用 parseFloat 转换,并验证是否完全匹配数字格式
- num = parseFloat(trimmed);
-
- // 如果转换后不是NaN,且字符串完全等于转换后的数字字符串(确保没有多余字符)
- if (isNaN(num) || trimmed !== String(num)) {
- return false;
- }
- }
- // 其他类型(如 null、object 等)直接判定为无效
- else {
- return false;
- }
-
- // 步骤2:检查数字是否在范围内(包含边界)
- return num >= min && num <= max;
- },
- /**
- * 验证表单数据
- * @param {Array} forms - 表单数据数组
- * @returns {Object} - 验证结果 { isValid: Boolean, errorMsg: String }
- */
- validateForm(forms) {
- // 1. 检查是否存在特殊项(包含 options 且有 checked=true)
- const specialItem = forms.find(item =>
- Array.isArray(item.options) &&
- item.options.some(option => option.checked === true)
- );
-
- // 如果存在特殊项被勾选,直接返回总分0
- if (specialItem) {
- forms.map(item=>item.score = 0)
- return { isValid: true, totalScore: 0, errorMsg: '' };
- }
-
- // 2. 验证其他项的分数是否有效(在 min 和 max 之间)
- for (const item of forms) {
- // 跳过特殊项
- if (Array.isArray(item.options)) continue;
-
- const { score, min, max } = item;
- const parsedScore = parseFloat(score);
-
- // 检查分数是否为有效数字
- if (isNaN(parsedScore) || !isFinite(parsedScore)) {
- return { isValid: false, totalScore: 0, errorMsg: `请输入有效的分数:${item.ask}` };
- }
-
- // 检查分数是否在范围内
- if (parsedScore < min || parsedScore > max) {
- return { isValid: false, totalScore: 0, errorMsg: `分数超出范围:${item.ask} (${min}-${max}分)` };
- }
- }
- // 3. 计算总分(当所有项都有效时)
- const totalScore = forms.reduce((sum, item) => {
- // 跳过特殊项(已处理)
- if (Array.isArray(item.options)) return sum;
-
- // 累加有效分数
- return sum + parseFloat(item.score);
- }, 0);
-
- return { isValid: true, totalScore, errorMsg: '' };
+ },
+ blurInput(item){
+ console.log("blur",item)
+ if(!this.validateNumberRange(item.score,item.min,item.max)){
+ uni.showToast({
+ title: "请输入正确的分值",
+ icon: "none"
+ })
+ item.score = 0
+ }
+ },
+ // 计算总分
+ calculateTotalScore(form) {
+ // 处理 forms 不存在或非数组的情况
+ if (!Array.isArray(this.form.forms)) {
+ this.form.total_score = 0;
+ return;
+ }
+ // 检查是否存在任何一个选项的 checked 为 true
+ const hasCheckedOption = this.form.forms.some(item => {
+ // 如果 item.options 存在且包含 checked 为 true 的项
+ return Array.isArray(item.options) &&
+ item.options.some(option => option.checked === true);
+ });
+
+ // 如果存在 checked 为 true 的选项,直接返回 0 分
+ if (hasCheckedOption) {
+ this.form.total_score = 0;
+ this.form.forms.map(item=>item.score=0)
+ return;
+ }
+ // 否则,正常累加所有项的分数
+ const total = this.form.forms.reduce((sum, item) => {
+ // 将 item.score 转换为数字,非数字默认 0
+ const score = Number(item.score) || 0;
+ return sum + score;
+ }, 0);
+
+ this.form.total_score = total;
+ },
+ // 判断是否为有效数子
+ validateNumberRange(value, min, max) {
+ // 步骤1:判断值是否为有效数字(处理字符串形式的数字)
+ let num;
+
+ // 如果已经是数字类型,直接使用
+ if (typeof value === 'number') {
+ num = value;
+ }
+ // 如果是字符串,尝试转换为数字
+ else if (typeof value === 'string') {
+ // 去除首尾空格
+ const trimmed = value.trim();
+
+ // 使用 parseFloat 转换,并验证是否完全匹配数字格式
+ num = parseFloat(trimmed);
+
+ // 如果转换后不是NaN,且字符串完全等于转换后的数字字符串(确保没有多余字符)
+ if (isNaN(num) || trimmed !== String(num)) {
+ return false;
+ }
+ }
+ // 其他类型(如 null、object 等)直接判定为无效
+ else {
+ return false;
+ }
+
+ // 步骤2:检查数字是否在范围内(包含边界)
+ return num >= min && num <= max;
+ },
+ /**
+ * 验证表单数据
+ * @param {Array} forms - 表单数据数组
+ * @returns {Object} - 验证结果 { isValid: Boolean, errorMsg: String }
+ */
+ validateForm(forms) {
+ // 1. 检查是否存在特殊项(包含 options 且有 checked=true)
+ const specialItem = forms.find(item =>
+ Array.isArray(item.options) &&
+ item.options.some(option => option.checked === true)
+ );
+
+ // 如果存在特殊项被勾选,直接返回总分0
+ if (specialItem) {
+ forms.map(item=>item.score = 0)
+ return { isValid: true, totalScore: 0, errorMsg: '' };
+ }
+
+ // 2. 验证其他项的分数是否有效(在 min 和 max 之间)
+ for (const item of forms) {
+ // 跳过特殊项
+ if (Array.isArray(item.options)) continue;
+
+ const { score, min, max } = item;
+ const parsedScore = parseFloat(score);
+
+ // 检查分数是否为有效数字
+ if (isNaN(parsedScore) || !isFinite(parsedScore)) {
+ return { isValid: false, totalScore: 0, errorMsg: `请输入有效的分数:${item.ask}` };
+ }
+
+ // 检查分数是否在范围内
+ if (parsedScore < min || parsedScore > max) {
+ return { isValid: false, totalScore: 0, errorMsg: `分数超出范围:${item.ask} (${min}-${max}分)` };
+ }
+ }
+ // 3. 计算总分(当所有项都有效时)
+ const totalScore = forms.reduce((sum, item) => {
+ // 跳过特殊项(已处理)
+ if (Array.isArray(item.options)) return sum;
+
+ // 累加有效分数
+ return sum + parseFloat(item.score);
+ }, 0);
+
+ return { isValid: true, totalScore, errorMsg: '' };
},
submit() {
- console.log("this.form", this.form)
- const res = this.validateForm(this.form.forms)
- if(!res.isValid){
- uni.showToast({
- title:res.errorMsg,
- icon:'none',
- })
- return
+ console.log("this.form", this.form)
+ const res = this.validateForm(this.form.forms)
+ if(!res.isValid){
+ uni.showToast({
+ title:res.errorMsg,
+ icon:'none',
+ })
+ return
}
const uploadSignImage = () => {
return new Promise((resolve, reject) => {
@@ -541,13 +647,13 @@
icon: 'success',
title: '保存成功',
})
- setTimeout(() => {
- if(this.type==='edit'){
- uni.redirectTo({
- url:'/package_sub/pages/quality/qualityHistory'
- })
- }else{
- uni.navigateBack()
+ setTimeout(() => {
+ if(this.type==='edit'){
+ uni.redirectTo({
+ url:'/package_sub/pages/quality/qualityHistory'
+ })
+ }else{
+ uni.navigateBack()
}
this.$u.vuex('vuex_admin_sign_image', '')
@@ -570,7 +676,7 @@
this.detail = res;
for (let key in this.form) {
this.form[key] = res[key]
- }
+ }
this.form.customer_name = res.customer.name
console.log(this.form)
this.fileList = res.files.map(i => ({
diff --git a/package_sub/pages/index/index.vue b/package_sub/pages/index/index.vue
index 324405f..546575c 100644
--- a/package_sub/pages/index/index.vue
+++ b/package_sub/pages/index/index.vue
@@ -46,7 +46,7 @@
- 质控列表
+ 质控回访
diff --git a/package_sub/pages/quality/quality.vue b/package_sub/pages/quality/quality.vue
index d422af6..62bf821 100644
--- a/package_sub/pages/quality/quality.vue
+++ b/package_sub/pages/quality/quality.vue
@@ -6,17 +6,37 @@
刷新
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -24,9 +44,9 @@
回访次数 {{ item.quality_callbacks_count }} 次
- 回访次数 {{ 0 }} 次
+ 回访次数 {{ 0 }} 次
- {{ item.product_type.name }}
+ {{ item.product_type_name }}
@@ -65,7 +85,7 @@
- 联系人 {{ item.contact_name }}
+ 联系人 {{ item.contact_name?item.contact_name:'' }}
@@ -93,6 +113,11 @@
+
+
+
+ 附近客户
+
@@ -104,10 +129,20 @@ export default {
scrollTop: 0,
isShowCalendar: false,
inputStyle: {
- width: "600rpx",
+ width: "100%",
fontSize: "28rpx",
fontWeight: "500"
},
+ callbackDropdownOptions: [
+ { label: '全部', value: 'all' },
+ { label: '无回访', value: 0 },
+ { label: '有回访', value: 1 },
+ ],
+ deadDropdownOptions: [
+ { label: '全部', value: 'all' },
+ { label: '正常', value: 0 },
+ { label: '死亡', value: 1 },
+ ],
optionsStatus: [{
label: '全部',
value: ''
@@ -151,7 +186,12 @@ export default {
select: {
page_size: 10,
page: 1,
- keyword: ''
+ keyword: '',
+ village_name: '',
+ has_quality_callbacks: 'all',
+ is_dead: 'all',
+ lat: '',
+ lng: ''
},
}
},
@@ -161,7 +201,12 @@ export default {
this.select = {
page_size: 10,
page: 1,
- keyword: ''
+ keyword: '',
+ village_name: '',
+ has_quality_callbacks: 'all',
+ is_dead: 'all',
+ lat: '',
+ lng: ''
}
this.nursingList = []
this.getList()
@@ -175,8 +220,37 @@ export default {
this.select.page = 1
this.$u.debounce(this.getList, 1000)
},
+ villageInput(e) {
+ this.select.village_name = e
+
+ this.nursingList = []
+ this.select.page = 1
+ this.$u.debounce(this.getList, 1000)
+ },
+
+ async fetchNearby() {
+ try {
+ const res = await uni.getLocation({ type: 'gcj02', isHighAccuracy: true })
+ // uni.getLocation Promise 风格返回 [err, data]
+ const location = Array.isArray(res) ? res[1] : res
+ if (location && location.latitude && location.longitude) {
+ this.select.lat = location.latitude
+ this.select.lng = location.longitude
+ this.nursingList = []
+ this.select.page = 1
+ this.getList()
+ } else {
+ uni.showToast({ icon: 'none', title: '定位失败,请稍后重试' })
+ }
+ } catch (e) {
+ uni.showToast({ icon: 'none', title: '未能获取定位权限' })
+ }
+ },
async getList() {
- const response = await this.$u.api.adminCustomerList(this.select)
+ const params = { ...this.select }
+ if (params.has_quality_callbacks === 'all') params.has_quality_callbacks = ''
+ if (params.is_dead === 'all') params.is_dead = ''
+ const response = await this.$u.api.adminCustomerList(params)
console.log("response",response)
let res = response.data
if (res.data.length > 0 && res.data) {
@@ -221,6 +295,16 @@ export default {
}
},
watch: {
+ 'select.has_quality_callbacks'(val) {
+ this.nursingList = []
+ this.select.page = 1
+ this.getList()
+ },
+ 'select.is_dead'(val) {
+ this.nursingList = []
+ this.select.page = 1
+ this.getList()
+ }
},
onReachBottom() {
this.select.page++
@@ -241,11 +325,11 @@ export default {