补充profile页面功能区功能

This commit is contained in:
2026-06-08 15:39:09 +08:00
parent 48fb499138
commit de62513987
8 changed files with 3756 additions and 1399 deletions

View File

@@ -0,0 +1,470 @@
{
"pages": [
{
"path": "pages/main/index",
"style": {
"navigationBarTitleText": "首页",
"navigationStyle": "custom",
"enablePullDownRefresh": false
}
},
{
"path": "pages/user/boot",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/user/login",
"style": {
"navigationBarTitleText": "用户登录",
"navigationStyle": "custom"
}
},
{
"path": "pages/user/register",
"style": {
"navigationBarTitleText": "注册"
}
},
{
"path": "pages/user/forgot-password",
"style": {
"navigationBarTitleText": "忘记密码"
}
},
{
"path": "pages/user/terms",
"style": {
"navigationBarTitleText": "用户协议与隐私政策"
}
},
{
"path": "pages/user/center",
"style": {
"navigationBarTitleText": "用户中心"
}
},
{
"path": "pages/user/profile",
"style": {
"navigationBarTitleText": "个人资料"
}
},
{
"path": "pages/user/change-password",
"style": {
"navigationBarTitleText": "修改密码"
}
},
{
"path": "pages/user/bind-phone",
"style": {
"navigationBarTitleText": "绑定手机"
}
},
{
"path": "pages/user/bind-email",
"style": {
"navigationBarTitleText": "绑定邮箱"
}
},
{
"path": "pages/main/messages",
"style": {
"navigationBarTitleText": "消息",
"enablePullDownRefresh": true
}
},
{
"path": "pages/main/cart",
"style": {
"navigationBarTitleText": "购物车",
"navigationStyle": "custom"
}
},
{
"path": "pages/main/profile",
"style": {
"navigationBarTitleText": "我的",
"navigationStyle": "custom"
}
},
{
"path": "pages/main/category",
"style": {
"navigationBarTitleText": "分类",
"navigationStyle": "custom"
}
}
],
"subPackages": [
{
"root": "pages/mall/consumer",
"pages": [
{
"path": "settings",
"style": {
"navigationBarTitleText": "设置"
}
},
{
"path": "edit-profile",
"style": {
"navigationBarTitleText": "编辑资料"
}
},
{
"path": "wallet",
"style": {
"navigationBarTitleText": "我的钱包"
}
},
{
"path": "withdraw",
"style": {
"navigationBarTitleText": "余额提现"
}
},
{
"path": "search",
"style": {
"navigationBarTitleText": "搜索",
"navigationStyle": "custom"
}
},
{
"path": "product-detail",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
},
{
"path": "channel-detail",
"style": {
"navigationBarTitleText": "频道详情",
"navigationStyle": "custom"
}
},
{
"path": "shop-detail",
"style": {
"navigationBarTitleText": "店铺详情"
}
},
{
"path": "coupons",
"style": {
"navigationBarTitleText": "我的优惠券"
}
},
{
"path": "favorites",
"style": {
"navigationBarTitleText": "我的收藏"
}
},
{
"path": "footprint",
"style": {
"navigationBarTitleText": "我的足迹"
}
},
{
"path": "address",
"style": {
"navigationBarTitleText": "地址"
}
},
{
"path": "address-list",
"style": {
"navigationBarTitleText": "收货地址"
}
},
{
"path": "address-edit",
"style": {
"navigationBarTitleText": "编辑地址"
}
},
{
"path": "checkout",
"style": {
"navigationBarTitleText": "确认订单"
}
},
{
"path": "payment",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
},
{
"path": "payment-success",
"style": {
"navigationBarTitleText": "支付成功",
"navigationStyle": "custom"
}
},
{
"path": "orders",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom",
"enablePullDownRefresh": true,
"backgroundColor": "#f5f5f5"
}
},
{
"path": "order-detail",
"style": {
"navigationBarTitleText": "订单详情"
}
},
{
"path": "logistics",
"style": {
"navigationBarTitleText": "物流详情"
}
},
{
"path": "review",
"style": {
"navigationBarTitleText": "评价晒单"
}
},
{
"path": "refund",
"style": {
"navigationBarTitleText": "退款/售后"
}
},
{
"path": "apply-refund",
"style": {
"navigationBarTitleText": "申请售后"
}
},
{
"path": "refund-review",
"style": {
"navigationBarTitleText": "服务评价"
}
},
{
"path": "chat",
"style": {
"navigationBarTitleText": "客服聊天",
"navigationStyle": "custom"
}
},
{
"path": "chat_new",
"style": {
"navigationBarTitleText": "客服聊天(新版)"
}
},
{
"path": "subscription/plan-list",
"style": {
"navigationBarTitleText": "软件订阅"
}
},
{
"path": "subscription/plan-detail",
"style": {
"navigationBarTitleText": "订阅详情"
}
},
{
"path": "subscription/subscribe-checkout",
"style": {
"navigationBarTitleText": "确认订阅"
}
},
{
"path": "subscription/my-subscriptions",
"style": {
"navigationBarTitleText": "我的订阅"
}
},
{
"path": "subscription/followed-shops",
"style": {
"navigationBarTitleText": "关注店铺"
}
},
{
"path": "points/index",
"style": {
"navigationBarTitleText": "积分管理"
}
},
{
"path": "points/signin",
"style": {
"navigationBarTitleText": "签到"
}
},
{
"path": "points/exchange",
"style": {
"navigationBarTitleText": "积分兑换"
}
},
{
"path": "points/exchange-records",
"style": {
"navigationBarTitleText": "兑换记录"
}
},
{
"path": "red-packets/index",
"style": {
"navigationBarTitleText": "我的红包"
}
},
{
"path": "bank-cards/index",
"style": {
"navigationBarTitleText": "银行卡管理"
}
},
{
"path": "bank-cards/add",
"style": {
"navigationBarTitleText": "添加银行卡"
}
},
{
"path": "home-service/index",
"style": {
"navigationBarTitleText": "居家上门服务",
"navigationStyle": "custom"
}
},
{
"path": "home-service/apply",
"style": {
"navigationBarTitleText": "提交服务申请",
"navigationStyle": "custom"
}
},
{
"path": "home-service/service-detail",
"style": {
"navigationBarTitleText": "预约服务",
"navigationStyle": "custom"
}
},
{
"path": "home-service/order-detail",
"style": {
"navigationBarTitleText": "服务单详情",
"navigationStyle": "custom"
}
},
{
"path": "home-service/feedback",
"style": {
"navigationBarTitleText": "验收反馈",
"navigationStyle": "custom"
}
},
{
"path": "bank-cards/verify",
"style": {
"navigationBarTitleText": "银行卡验证"
}
},
{
"path": "balance/index",
"style": {
"navigationBarTitleText": "余额"
}
},
{
"path": "my-reviews",
"style": {
"navigationBarTitleText": "我的评价"
}
},
{
"path": "message-detail",
"style": {
"navigationBarTitleText": "消息详情"
}
},
{
"path": "message-batch-delete",
"style": {
"navigationBarTitleText": "批量删除"
}
},
{
"path": "member/index",
"style": {
"navigationBarTitleText": "会员中心"
}
},
{
"path": "product-reviews",
"style": {
"navigationBarTitleText": "商品评价"
}
}
]
}
],
"tabBar": {
"color": "#999999",
"selectedColor": "#ff5000",
"backgroundColor": "#ffffff",
"borderStyle": "black",
"list": [
{
"pagePath": "pages/main/index",
"text": "首页",
"iconPath": "static/tabbar/home.png",
"selectedIconPath": "static/tabbar/home-active.png"
},
{
"pagePath": "pages/main/messages",
"text": "消息",
"iconPath": "static/tabbar/message.png",
"selectedIconPath": "static/tabbar/message.png"
},
{
"pagePath": "pages/main/cart",
"text": "购物车",
"iconPath": "static/tabbar/cart.png",
"selectedIconPath": "static/tabbar/cart.png"
},
{
"pagePath": "pages/main/profile",
"text": "我的",
"iconPath": "static/tabbar/user.png",
"selectedIconPath": "static/tabbar/user.png"
}
]
},
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "mall",
"navigationBarBackgroundColor": "#FFFFFF",
"backgroundColor": "#F8F8F8"
},
"condition": {
"current": 0,
"list": [
{
"name": "consumer端",
"path": "pages/main/index",
"query": "role=consumer"
}
]
}
}

View File

@@ -0,0 +1,607 @@
-- Homecare location-distance RPCs (precheck + submit) — Fixed for uuid types
-- Date: 2026-06-03 (original), 2026-06-08 (fixed for uuid alignment)
-- Purpose: move checkin distance flow to RPC-only for phase1 tables
-- Fixed: p_org_id, p_team_id, p_operator_id changed from text to uuid
-- to match hc_dispatch_assignments table schema
create or replace function public.rpc_homecare_dispatch_assignment_upsert(
p_work_order_id uuid,
p_org_id uuid, -- ✅ fixed: was text, now uuid
p_team_id uuid, -- ✅ fixed: was text, now uuid
p_worker_id uuid,
p_service_latitude numeric,
p_service_longitude numeric,
p_service_coordinate_type text default 'gcj02',
p_operator_id uuid default null, -- ✅ fixed: was text, now uuid
p_dispatch_reason text default null
)
returns jsonb
language plpgsql
security definer
set search_path = public, app
as $$
declare
v_prev_version integer := 0;
v_prev_id text;
begin
if p_work_order_id is null then
raise exception 'p_work_order_id is required';
end if;
if p_org_id is null then
raise exception 'p_org_id is required';
end if;
if p_service_latitude is null or p_service_longitude is null then
raise exception 'service coordinates are required';
end if;
if to_regclass('public.hc_dispatch_assignments') is null then
raise exception 'hc_dispatch_assignments table is required';
end if;
select id, assign_version
into v_prev_id, v_prev_version
from public.hc_dispatch_assignments
where work_order_id = p_work_order_id
and is_current = true
order by created_at desc
limit 1;
if found then
update public.hc_dispatch_assignments
set is_current = false,
updated_at = now(),
updated_by = coalesce(p_operator_id, updated_by)
where id = v_prev_id;
end if;
insert into public.hc_dispatch_assignments(
id,
work_order_id,
org_id,
team_id,
assign_version,
worker_id,
service_latitude,
service_longitude,
service_coordinate_type,
dispatch_status,
dispatch_reason,
is_current,
dispatched_at,
created_by,
updated_by,
created_at,
updated_at
) values (
'dsp_' || gen_random_uuid()::text,
p_work_order_id,
p_org_id,
p_team_id,
v_prev_version + 1,
p_worker_id,
p_service_latitude,
p_service_longitude,
lower(coalesce(p_service_coordinate_type, 'gcj02')),
'PENDING',
p_dispatch_reason,
true,
now(),
p_operator_id,
p_operator_id,
now(),
now()
);
return jsonb_build_object('ok', true, 'assignVersion', v_prev_version + 1);
end;
$$;
create or replace function public.rpc_homecare_checkin_precheck(
p_work_order_id uuid,
p_worker_id uuid,
p_latitude numeric,
p_longitude numeric,
p_coordinate_type text default 'gcj02',
p_accuracy numeric default null,
p_reported_at timestamptz default null,
p_location_scene text default 'CHECKIN_PRECHECK'
)
returns jsonb
language plpgsql
security definer
set search_path = public, app
as $$
declare
v_assignment public.hc_dispatch_assignments%rowtype;
v_radius_meters numeric := 200;
v_distance_meters numeric;
v_can_checkin boolean := false;
v_reason_code text := 'WORK_ORDER_NOT_ASSIGNABLE';
v_scene text := upper(coalesce(p_location_scene, 'CHECKIN_PRECHECK'));
v_reported_at timestamptz := coalesce(p_reported_at, now());
v_worker_location_accepted boolean := false;
v_service_location_ready boolean := false;
v_insert_location boolean := false;
v_lat1 double precision;
v_lng1 double precision;
v_lat2 double precision;
v_lng2 double precision;
v_a double precision;
v_c double precision;
begin
if p_work_order_id is null then
raise exception 'p_work_order_id is required';
end if;
if p_worker_id is null then
raise exception 'p_worker_id is required';
end if;
if p_latitude is null or p_longitude is null then
raise exception 'p_latitude and p_longitude are required';
end if;
if v_scene not in ('CHECKIN_PRECHECK', 'CHECKIN') then
raise exception 'invalid p_location_scene: %', v_scene;
end if;
if to_regclass('public.hc_dispatch_assignments') is null then
raise exception 'hc_dispatch_assignments table is required';
end if;
if to_regclass('public.hc_worker_locations') is null then
raise exception 'hc_worker_locations table is required';
end if;
if to_regclass('public.sys_sla_config') is null then
raise exception 'sys_sla_config table is required';
end if;
select *
into v_assignment
from public.hc_dispatch_assignments
where work_order_id = p_work_order_id
and is_current = true
order by created_at desc
limit 1;
if not found then
v_reason_code := 'SERVICE_LOCATION_MISSING';
return jsonb_build_object(
'distanceMeters', null,
'allowedRadiusMeters', 0,
'canCheckin', false,
'reasonCode', v_reason_code,
'workerLocationAccepted', false,
'serviceLocationReady', false
);
end if;
if coalesce(v_assignment.dispatch_status, '') not in ('PENDING', 'ACCEPTED') then
v_reason_code := 'WORK_ORDER_NOT_ASSIGNABLE';
return jsonb_build_object(
'distanceMeters', null,
'allowedRadiusMeters', 0,
'canCheckin', false,
'reasonCode', v_reason_code,
'workerLocationAccepted', false,
'serviceLocationReady', false
);
end if;
if v_assignment.worker_id is not null and v_assignment.worker_id <> p_worker_id then
v_reason_code := 'WORKER_NOT_MATCHED';
return jsonb_build_object(
'distanceMeters', null,
'allowedRadiusMeters', 0,
'canCheckin', false,
'reasonCode', v_reason_code,
'workerLocationAccepted', false,
'serviceLocationReady', true
);
end if;
if v_assignment.service_latitude is null or v_assignment.service_longitude is null then
v_reason_code := 'SERVICE_LOCATION_MISSING';
return jsonb_build_object(
'distanceMeters', null,
'allowedRadiusMeters', 0,
'canCheckin', false,
'reasonCode', v_reason_code,
'workerLocationAccepted', false,
'serviceLocationReady', false
);
end if;
v_service_location_ready := true;
-- SLA lookup priority: WORK_ORDER > ORG > TEAM > GLOBAL
select config_value::numeric
into v_radius_meters
from public.sys_sla_config
where config_key = 'HOMECARE_CHECKIN_RADIUS_METERS'
and scope_type = 'WORK_ORDER'
and scope_id = p_work_order_id::text
and is_active = true
limit 1;
if v_radius_meters is null and v_assignment.org_id is not null then
select config_value::numeric
into v_radius_meters
from public.sys_sla_config
where config_key = 'HOMECARE_CHECKIN_RADIUS_METERS'
and scope_type = 'ORG'
and scope_id = v_assignment.org_id::text
and is_active = true
limit 1;
end if;
if v_radius_meters is null and v_assignment.team_id is not null then
select config_value::numeric
into v_radius_meters
from public.sys_sla_config
where config_key = 'HOMECARE_CHECKIN_RADIUS_METERS'
and scope_type = 'TEAM'
and scope_id = v_assignment.team_id::text
and is_active = true
limit 1;
end if;
if v_radius_meters is null then
select config_value::numeric
into v_radius_meters
from public.sys_sla_config
where config_key = 'HOMECARE_CHECKIN_RADIUS_METERS'
and scope_type = 'GLOBAL'
and scope_id is null
and is_active = true
limit 1;
end if;
v_radius_meters := coalesce(v_radius_meters, 200);
-- Haversine distance
v_lat1 := radians(p_latitude::double precision);
v_lng1 := radians(p_longitude::double precision);
v_lat2 := radians(v_assignment.service_latitude::double precision);
v_lng2 := radians(v_assignment.service_longitude::double precision);
v_a := power(sin((v_lat2 - v_lat1) / 2), 2)
+ cos(v_lat1) * cos(v_lat2) * power(sin((v_lng2 - v_lng1) / 2), 2);
v_c := 2 * atan2(sqrt(v_a), sqrt(1 - v_a));
v_distance_meters := round((6371000 * v_c)::numeric, 2);
v_can_checkin := (v_distance_meters <= v_radius_meters);
v_reason_code := case when v_can_checkin then 'OK' else 'OUT_OF_RADIUS' end;
v_insert_location := (v_scene = 'CHECKIN_PRECHECK') or (v_scene = 'CHECKIN');
if v_insert_location then
insert into public.hc_worker_locations(
id,
work_order_id,
worker_id,
latitude,
longitude,
coordinate_type,
accuracy,
location_scene,
reported_at,
distance_meters,
created_at
) values (
'loc_' || gen_random_uuid()::text,
p_work_order_id,
p_worker_id,
p_latitude,
p_longitude,
lower(coalesce(p_coordinate_type, 'gcj02')),
p_accuracy,
v_scene,
v_reported_at,
v_distance_meters,
now()
);
v_worker_location_accepted := true;
end if;
return jsonb_build_object(
'distanceMeters', v_distance_meters,
'allowedRadiusMeters', v_radius_meters,
'canCheckin', v_can_checkin,
'reasonCode', v_reason_code,
'workerLocationAccepted', v_worker_location_accepted,
'serviceLocationReady', v_service_location_ready
);
end;
$$;
create or replace function public.rpc_homecare_checkin_submit(
p_work_order_id uuid,
p_worker_id uuid,
p_latitude numeric,
p_longitude numeric,
p_coordinate_type text default 'gcj02',
p_accuracy numeric default null,
p_reported_at timestamptz default null,
p_evidence_file_ids jsonb default '[]'::jsonb,
p_signature_payload text default null,
p_reason text default null
)
returns jsonb
language plpgsql
security definer
set search_path = public, app
as $$
declare
v_precheck jsonb;
v_can_checkin boolean := false;
v_assignment public.hc_dispatch_assignments%rowtype;
v_assignment_found boolean := false;
v_max_photo_count numeric := 3;
v_requested_count integer := 0;
v_existing_count integer := 0;
v_mismatch_count integer := 0;
v_not_owned_count integer := 0;
v_not_ready_count integer := 0;
begin
if to_regclass('public.hc_work_order_confirmations') is null then
raise exception 'hc_work_order_confirmations table is required';
end if;
if p_signature_payload is null or length(trim(p_signature_payload)) = 0 then
return jsonb_build_object(
'distanceMeters', null,
'allowedRadiusMeters', 0,
'canCheckin', false,
'reasonCode', 'SIGNATURE_REQUIRED',
'workerLocationAccepted', false,
'serviceLocationReady', false
);
end if;
if length(trim(p_signature_payload)) < 8 then
return jsonb_build_object(
'distanceMeters', null,
'allowedRadiusMeters', 0,
'canCheckin', false,
'reasonCode', 'SIGNATURE_INVALID',
'workerLocationAccepted', false,
'serviceLocationReady', false
);
end if;
if p_evidence_file_ids is null or jsonb_typeof(p_evidence_file_ids) <> 'array' then
return jsonb_build_object(
'distanceMeters', null,
'allowedRadiusMeters', 0,
'canCheckin', false,
'reasonCode', 'EVIDENCE_FILE_NOT_EXIST',
'workerLocationAccepted', false,
'serviceLocationReady', false
);
end if;
v_requested_count := jsonb_array_length(p_evidence_file_ids);
if v_requested_count < 1 then
return jsonb_build_object(
'distanceMeters', null,
'allowedRadiusMeters', 0,
'canCheckin', false,
'reasonCode', 'EVIDENCE_FILE_NOT_EXIST',
'workerLocationAccepted', false,
'serviceLocationReady', false
);
end if;
select *
into v_assignment
from public.hc_dispatch_assignments
where work_order_id = p_work_order_id
and is_current = true
order by created_at desc
limit 1;
v_assignment_found := found;
select config_value::numeric
into v_max_photo_count
from public.sys_sla_config
where config_key = 'HOMECARE_CHECKIN_MAX_PHOTO_COUNT'
and scope_type = 'WORK_ORDER'
and scope_id = p_work_order_id::text
and is_active = true
limit 1;
if v_max_photo_count is null and v_assignment_found and v_assignment.org_id is not null then
select config_value::numeric
into v_max_photo_count
from public.sys_sla_config
where config_key = 'HOMECARE_CHECKIN_MAX_PHOTO_COUNT'
and scope_type = 'ORG'
and scope_id = v_assignment.org_id::text
and is_active = true
limit 1;
end if;
if v_max_photo_count is null and v_assignment_found and v_assignment.team_id is not null then
select config_value::numeric
into v_max_photo_count
from public.sys_sla_config
where config_key = 'HOMECARE_CHECKIN_MAX_PHOTO_COUNT'
and scope_type = 'TEAM'
and scope_id = v_assignment.team_id::text
and is_active = true
limit 1;
end if;
if v_max_photo_count is null then
select config_value::numeric
into v_max_photo_count
from public.sys_sla_config
where config_key = 'HOMECARE_CHECKIN_MAX_PHOTO_COUNT'
and scope_type = 'GLOBAL'
and scope_id is null
and is_active = true
limit 1;
end if;
v_max_photo_count := coalesce(v_max_photo_count, 3);
if v_requested_count > v_max_photo_count then
return jsonb_build_object(
'distanceMeters', null,
'allowedRadiusMeters', 0,
'canCheckin', false,
'reasonCode', 'PHOTO_COUNT_EXCEED_LIMIT',
'workerLocationAccepted', false,
'serviceLocationReady', false
);
end if;
select count(*)
into v_existing_count
from public.hc_evidence_files e
where e.id in (select jsonb_array_elements_text(p_evidence_file_ids));
if v_existing_count <> v_requested_count then
return jsonb_build_object(
'distanceMeters', null,
'allowedRadiusMeters', 0,
'canCheckin', false,
'reasonCode', 'EVIDENCE_FILE_NOT_EXIST',
'workerLocationAccepted', false,
'serviceLocationReady', false
);
end if;
select count(*)
into v_mismatch_count
from public.hc_evidence_files e
where e.id in (select jsonb_array_elements_text(p_evidence_file_ids))
and e.work_order_id <> p_work_order_id;
if v_mismatch_count > 0 then
return jsonb_build_object(
'distanceMeters', null,
'allowedRadiusMeters', 0,
'canCheckin', false,
'reasonCode', 'EVIDENCE_FILE_WORK_ORDER_MISMATCH',
'workerLocationAccepted', false,
'serviceLocationReady', false
);
end if;
select count(*)
into v_not_owned_count
from public.hc_evidence_files e
where e.id in (select jsonb_array_elements_text(p_evidence_file_ids))
and e.work_order_id = p_work_order_id
and coalesce(e.uploader_id, '') <> p_worker_id::text;
if v_not_owned_count > 0 then
return jsonb_build_object(
'distanceMeters', null,
'allowedRadiusMeters', 0,
'canCheckin', false,
'reasonCode', 'EVIDENCE_FILE_NOT_OWNED',
'workerLocationAccepted', false,
'serviceLocationReady', false
);
end if;
select count(*)
into v_not_ready_count
from public.hc_evidence_files e
where e.id in (select jsonb_array_elements_text(p_evidence_file_ids))
and e.work_order_id = p_work_order_id
and (e.file_url is null or length(trim(e.file_url)) = 0);
if v_not_ready_count > 0 then
return jsonb_build_object(
'distanceMeters', null,
'allowedRadiusMeters', 0,
'canCheckin', false,
'reasonCode', 'EVIDENCE_FILE_NOT_READY',
'workerLocationAccepted', false,
'serviceLocationReady', false
);
end if;
v_precheck := public.rpc_homecare_checkin_precheck(
p_work_order_id,
p_worker_id,
p_latitude,
p_longitude,
p_coordinate_type,
p_accuracy,
p_reported_at,
'CHECKIN'
);
v_can_checkin := coalesce((v_precheck ->> 'canCheckin')::boolean, false);
if not v_can_checkin then
return v_precheck;
end if;
insert into public.hc_work_order_confirmations(
id,
work_order_id,
confirmation_type,
status,
confirmed_by,
confirmed_at,
reason,
payload,
created_at,
updated_at
) values (
'woc_' || gen_random_uuid()::text,
p_work_order_id,
'ARRIVAL',
'PENDING',
p_worker_id,
null,
coalesce(p_reason, 'worker_checkin_submitted'),
jsonb_build_object(
'distanceMeters', (v_precheck ->> 'distanceMeters')::numeric,
'allowedRadiusMeters', (v_precheck ->> 'allowedRadiusMeters')::numeric,
'coordinateType', coalesce(lower(p_coordinate_type), 'gcj02'),
'reportedAt', coalesce(p_reported_at, now()),
'signatureProvided', (p_signature_payload is not null and length(trim(p_signature_payload)) > 0),
'evidenceFileIds', coalesce(p_evidence_file_ids, '[]'::jsonb)
),
now(),
now()
);
return v_precheck || jsonb_build_object('confirmationInserted', true);
end;
$$;
-- ============================================================
-- Grants (updated for uuid types)
-- ============================================================
revoke all on function public.rpc_homecare_checkin_precheck(uuid, uuid, numeric, numeric, text, numeric, timestamptz, text) from public;
grant execute on function public.rpc_homecare_checkin_precheck(uuid, uuid, numeric, numeric, text, numeric, timestamptz, text) to service_role;
revoke all on function public.rpc_homecare_dispatch_assignment_upsert(uuid, uuid, uuid, uuid, numeric, numeric, text, uuid, text) from public;
grant execute on function public.rpc_homecare_dispatch_assignment_upsert(uuid, uuid, uuid, uuid, numeric, numeric, text, uuid, text) to service_role;
revoke all on function public.rpc_homecare_checkin_submit(uuid, uuid, numeric, numeric, text, numeric, timestamptz, jsonb, text, text) from public;
grant execute on function public.rpc_homecare_checkin_submit(uuid, uuid, numeric, numeric, text, numeric, timestamptz, jsonb, text, text) to service_role;
-- ============================================================
-- Comments
-- ============================================================
comment on function public.rpc_homecare_checkin_precheck(uuid, uuid, numeric, numeric, text, numeric, timestamptz, text)
is 'Homecare checkin distance precheck RPC with SLA lookup and optional location snapshot insert';
comment on function public.rpc_homecare_dispatch_assignment_upsert(uuid, uuid, uuid, uuid, numeric, numeric, text, uuid, text)
is 'Homecare dispatch assignment upsert RPC; rotates current assignment and snapshots service location for distance checkin (uuid types)';
comment on function public.rpc_homecare_checkin_submit(uuid, uuid, numeric, numeric, text, numeric, timestamptz, jsonb, text, text)
is 'Homecare checkin submit RPC; re-runs distance precheck and writes confirmation snapshot when checkin is allowed';

View File

@@ -0,0 +1,916 @@
-- ============================================================
-- Homecare Migration SQL - Phase 1 (Location & Checkin)
-- Date: 2026-06-05
-- Purpose: Formal migration for homecare location distance phase 1
-- Scope: Create/alter tables, indexes, foreign keys, seed data
-- ID Type: All user/org/team/elder IDs unified to uuid
-- Checkin Radius: 50 meters (phase 1)
-- ============================================================
BEGIN;
-- Needed for gen_random_uuid() defaults used by UUID primary keys.
CREATE EXTENSION IF NOT EXISTS pgcrypto;
-- ============================================================
-- Part 1: Create/Alter Foundation Tables
-- ============================================================
-- Table: public.hc_work_order_events
-- Source: 20260518_homecare_foundation.sql
CREATE TABLE IF NOT EXISTS public.hc_work_order_events (
id text primary key,
work_order_id uuid not null,
org_id uuid not null,
team_id uuid,
worker_id uuid,
action text not null,
from_status text not null,
to_status text not null,
operator_id uuid not null,
reason text,
payload jsonb,
created_at timestamptz not null default now()
);
-- Table: public.hc_work_order_exceptions
-- Source: 20260518_homecare_foundation.sql
CREATE TABLE IF NOT EXISTS public.hc_work_order_exceptions (
id text primary key,
work_order_id uuid not null,
org_id uuid not null,
team_id uuid,
worker_id uuid,
exception_type text not null,
description text not null,
status text not null default 'PENDING' check (status in ('PENDING', 'HANDLED')),
decision text,
reason text,
created_by uuid not null,
handled_by uuid,
evidence_urls jsonb not null default '[]'::jsonb,
created_at timestamptz not null default now(),
handled_at timestamptz
);
-- Table: public.hc_evidence_files
-- Source: 20260518_homecare_foundation.sql
-- Phase 1 Update: Add fields for checkin evidence support
CREATE TABLE IF NOT EXISTS public.hc_evidence_files (
id text primary key,
work_order_id uuid not null,
org_id uuid not null,
owner_id uuid,
uploader_id uuid not null,
file_url text not null,
mime_type text,
evidence_type text,
-- Phase 1新增字段
file_size_bytes bigint not null default 0,
upload_status text not null default 'TEMP' check (upload_status in ('TEMP', 'READY', 'BOUND', 'LOCKED', 'DELETED')),
storage_bucket text,
storage_path text,
file_hash text,
bound_action text check (bound_action in ('CHECKIN', 'CHECKOUT', 'EXCEPTION')),
bound_record_id text,
is_locked boolean not null default false,
locked_at timestamptz,
expires_at timestamptz,
updated_at timestamptz not null default now(),
created_at timestamptz not null default now()
);
-- Table: public.hc_worker_qualifications
-- Source: 20260518_homecare_foundation.sql
CREATE TABLE IF NOT EXISTS public.hc_worker_qualifications (
id text primary key,
org_id uuid not null,
worker_id uuid not null,
qualification_type text not null,
qualification_no text,
issue_org text,
valid_from date,
valid_to date,
review_status text not null default 'PENDING' check (review_status in ('PENDING', 'APPROVED', 'REJECTED', 'EXPIRED')),
reviewed_by uuid,
reviewed_at timestamptz,
file_urls jsonb not null default '[]'::jsonb,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now()
);
-- Table: public.hc_settlements
-- Source: 20260518_homecare_foundation.sql
CREATE TABLE IF NOT EXISTS public.hc_settlements (
id uuid primary key default gen_random_uuid(),
work_order_id uuid not null unique,
org_id uuid not null,
finance_owner_id uuid,
status text not null default 'PENDING' check (status in ('PENDING', 'READY', 'CONFIRMED')),
amount numeric(12, 2) not null default 0,
currency text not null default 'CNY',
settled_at timestamptz,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now()
);
-- ============================================================
-- Part 2: Business Closure Extension Tables
-- ============================================================
-- Table: public.hc_acceptances
-- Source: 20260527_homecare_business_closure_extensions.sql
CREATE TABLE IF NOT EXISTS public.hc_acceptances (
id text primary key,
work_order_id uuid not null,
org_id uuid not null,
team_id uuid,
worker_id uuid,
status text not null default 'PENDING' check (status in ('PENDING', 'ACCEPTED', 'REJECTED', 'CLOSED')),
result text check (result in ('PASS', 'REJECT')),
rating smallint check (rating between 1 and 5),
tags jsonb not null default '[]'::jsonb,
comment text,
rejected_reason text,
accepted_by uuid,
accepted_at timestamptz,
created_by uuid,
updated_by uuid,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now()
);
-- Table: public.hc_acceptance_issues
-- Source: 20260527_homecare_business_closure_extensions.sql
CREATE TABLE IF NOT EXISTS public.hc_acceptance_issues (
id text primary key,
acceptance_id text not null,
work_order_id uuid not null,
org_id uuid not null,
team_id uuid,
worker_id uuid,
issue_type text not null,
priority text,
description text not null,
evidence_urls jsonb not null default '[]'::jsonb,
status text not null default 'OPEN' check (status in ('OPEN', 'PROCESSING', 'RESOLVED', 'CLOSED')),
reporter_id uuid not null,
handler_id uuid,
resolution text,
resolved_at timestamptz,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now()
);
-- Table: public.hc_complaints
-- Source: 20260527_homecare_business_closure_extensions.sql
CREATE TABLE IF NOT EXISTS public.hc_complaints (
id text primary key,
work_order_id uuid,
acceptance_issue_id text,
org_id uuid not null,
team_id uuid,
worker_id uuid,
complainant_id uuid not null,
complaint_type text not null,
description text not null,
evidence_urls jsonb not null default '[]'::jsonb,
status text not null default 'PENDING' check (status in ('PENDING', 'PROCESSING', 'RESOLVED', 'CLOSED')),
handler_id uuid,
resolution text,
resolved_at timestamptz,
created_by uuid,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now()
);
-- Table: public.hc_exception_actions
-- Source: 20260527_homecare_business_closure_extensions.sql
CREATE TABLE IF NOT EXISTS public.hc_exception_actions (
id text primary key,
exception_id text not null,
work_order_id uuid not null,
org_id uuid not null,
team_id uuid,
worker_id uuid,
action_type text not null,
description text,
payload jsonb not null default '{}'::jsonb,
operator_id uuid not null,
created_at timestamptz not null default now()
);
-- Table: public.hc_settlement_items
CREATE TABLE IF NOT EXISTS public.hc_settlement_items (
id uuid primary key default gen_random_uuid(),
settlement_id uuid not null,
work_order_id uuid not null,
execution_record_id uuid,
item_name text not null,
unit_price numeric(12, 2) not null default 0,
quantity numeric(10, 2) not null default 1,
actual_amount numeric(12, 2) not null default 0,
deduction_amount numeric(12, 2) not null default 0,
self_pay_amount numeric(12, 2) not null default 0,
status text not null default 'PENDING' check (status in ('PENDING', 'APPROVED', 'REJECTED', 'WAIVED')),
created_at timestamptz not null default now(),
updated_at timestamptz not null default now()
);
-- Table: public.hc_payments
-- Source: 20260527_homecare_business_closure_extensions.sql
CREATE TABLE IF NOT EXISTS public.hc_payments (
id uuid primary key default gen_random_uuid(),
settlement_id uuid not null,
org_id uuid not null,
payer_id uuid,
payment_channel text not null,
transaction_id text,
amount numeric(12, 2) not null,
status text not null default 'PENDING' check (status in ('PENDING', 'PAID', 'FAILED', 'CANCELLED', 'REFUNDING', 'REFUNDED')),
callback_payload jsonb not null default '{}'::jsonb,
paid_at timestamptz,
failed_at timestamptz,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now()
);
-- Table: public.hc_refunds
-- Source: 20260527_homecare_business_closure_extensions.sql
CREATE TABLE IF NOT EXISTS public.hc_refunds (
id uuid primary key default gen_random_uuid(),
payment_id uuid not null,
settlement_id uuid not null,
org_id uuid not null,
amount numeric(12, 2) not null,
reason text not null,
status text not null default 'PENDING' check (status in ('PENDING', 'PROCESSING', 'REFUNDED', 'FAILED', 'CLOSED')),
refund_transaction_id text,
created_by uuid,
refunded_at timestamptz,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now()
);
-- Table: public.hc_ledgers
-- Source: 20260527_homecare_business_closure_extensions.sql
CREATE TABLE IF NOT EXISTS public.hc_ledgers (
id uuid primary key default gen_random_uuid(),
settlement_id uuid not null,
org_id uuid not null,
archive_period text not null,
ledger_type text not null default 'SETTLEMENT',
content jsonb not null default '{}'::jsonb,
archived_at timestamptz,
created_by uuid,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now()
);
-- Table: public.hc_archive_files
-- Source: 20260527_homecare_business_closure_extensions.sql
CREATE TABLE IF NOT EXISTS public.hc_archive_files (
id uuid primary key default gen_random_uuid(),
ledger_id uuid not null,
org_id uuid not null,
file_url text not null,
file_name text,
file_size bigint,
mime_type text,
created_by uuid,
created_at timestamptz not null default now()
);
-- Table: public.hc_consent_records
-- Source: 20260527_homecare_business_closure_extensions.sql
CREATE TABLE IF NOT EXISTS public.hc_consent_records (
id text primary key,
org_id uuid not null,
elder_id uuid not null,
consent_type text not null,
consent_status text not null default 'GRANTED' check (consent_status in ('GRANTED', 'REVOKED', 'EXPIRED')),
consent_version text,
granted_by uuid,
granted_at timestamptz,
revoked_at timestamptz,
expires_at timestamptz,
evidence_urls jsonb not null default '[]'::jsonb,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now()
);
-- ============================================================
-- Part 3: Location Distance Phase 1 Tables
-- ============================================================
-- Table: public.hc_dispatch_assignments
-- Source: 20260602_homecare_location_distance_phase1_tables.sql
CREATE TABLE IF NOT EXISTS public.hc_dispatch_assignments (
id text primary key,
work_order_id uuid not null,
org_id uuid not null,
team_id uuid,
assign_version integer not null default 1 check (assign_version > 0),
worker_id uuid,
service_latitude numeric(10,7) not null,
service_longitude numeric(10,7) not null,
service_coordinate_type text not null default 'gcj02',
dispatch_status text not null default 'PENDING'
check (dispatch_status in ('PENDING', 'ACCEPTED', 'REJECTED', 'TIMEOUT', 'REASSIGNED', 'CANCELLED')),
dispatch_reason text,
is_current boolean not null default true,
dispatched_at timestamptz not null default now(),
accepted_at timestamptz,
rejected_at timestamptz,
timeout_at timestamptz,
reassigned_at timestamptz,
cancelled_at timestamptz,
created_by uuid,
updated_by uuid,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now(),
constraint chk_hc_dispatch_assignments_lat_range check (service_latitude between -90 and 90),
constraint chk_hc_dispatch_assignments_lng_range check (service_longitude between -180 and 180),
constraint uq_hc_dispatch_assignments_version unique (work_order_id, assign_version)
);
-- Table: public.hc_worker_locations
-- Source: 20260602_homecare_location_distance_phase1_tables.sql
CREATE TABLE IF NOT EXISTS public.hc_worker_locations (
id text primary key,
work_order_id uuid not null,
worker_id uuid not null,
latitude numeric(10,7) not null,
longitude numeric(10,7) not null,
coordinate_type text not null default 'gcj02',
accuracy numeric(10,2),
location_scene text not null
check (location_scene in ('ON_THE_WAY', 'CHECKIN_PRECHECK', 'CHECKIN', 'CHECKOUT')),
reported_at timestamptz not null,
distance_meters numeric(10,2),
formatted_address text,
province text,
city text,
district text,
street text,
poi_title text,
geocode_provider text,
geocode_status text,
address_updated_at timestamptz,
created_at timestamptz not null default now(),
constraint chk_hc_worker_locations_lat_range check (latitude between -90 and 90),
constraint chk_hc_worker_locations_lng_range check (longitude between -180 and 180)
);
-- Table: public.hc_work_order_confirmations
-- Source: 20260602_homecare_location_distance_phase1_tables.sql
CREATE TABLE IF NOT EXISTS public.hc_work_order_confirmations (
id text primary key,
work_order_id uuid not null,
confirmation_type text not null default 'ARRIVAL',
status text not null default 'PENDING'
check (status in ('PENDING', 'CONFIRMED', 'REJECTED')),
confirmed_by uuid,
confirmed_at timestamptz,
reason text,
payload jsonb not null default '{}'::jsonb,
-- Phase 1预留二期电子签名扩展字段
signature_url text,
signature_hash text,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now()
);
-- Table: public.sys_sla_config
-- Source: 20260602_homecare_location_distance_phase1_tables.sql
CREATE TABLE IF NOT EXISTS public.sys_sla_config (
id text primary key,
config_key text not null,
config_value text not null,
value_type text not null default 'string',
scope_type text not null default 'GLOBAL'
check (scope_type in ('WORK_ORDER', 'ORG', 'TEAM', 'GLOBAL')),
scope_id text,
is_active boolean not null default true,
description text,
updated_by uuid,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now(),
constraint chk_sys_sla_config_scope check (
(scope_type = 'GLOBAL' and scope_id is null)
or (scope_type <> 'GLOBAL')
)
);
-- ============================================================
-- Part 4: Audit Extension Tables (if not exist)
-- ============================================================
-- Table: public.hc_state_transitions
CREATE TABLE IF NOT EXISTS public.hc_state_transitions (
id text primary key,
work_order_id uuid not null,
from_status text not null,
to_status text not null,
triggered_by uuid not null,
reason text,
payload jsonb,
created_at timestamptz not null default now()
);
-- Table: public.hc_audit_logs
CREATE TABLE IF NOT EXISTS public.hc_audit_logs (
id text primary key,
work_order_id uuid,
org_id uuid,
actor_id uuid not null,
actor_role text not null,
action text not null,
resource_type text not null,
resource_id text not null,
details jsonb,
ip_address inet,
user_agent text,
created_at timestamptz not null default now()
);
-- Table: public.hc_sensitive_access_logs
CREATE TABLE IF NOT EXISTS public.hc_sensitive_access_logs (
id text primary key,
accessor_id uuid not null,
accessed_resource_type text not null,
accessed_resource_id text not null,
access_purpose text not null,
approved_by uuid,
created_at timestamptz not null default now()
);
-- ============================================================
-- Part 4.5: Legacy Compatibility Fixes
-- ============================================================
-- These fixes make reruns safe when older hc_* tables already exist.
-- CREATE TABLE IF NOT EXISTS does not upgrade existing table definitions,
-- so referenced columns and FK columns are normalized before indexes/FKs.
-- Existing settlements table may already have id uuid in current database.
-- If settlement-related child tables already exist with text FK columns,
-- convert them to uuid before FK creation. This succeeds only when existing
-- values are empty or valid UUID strings.
DO $$
BEGIN
IF to_regclass('public.hc_settlement_items') IS NOT NULL
AND EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema='public'
AND table_name='hc_settlement_items'
AND column_name='settlement_id'
AND udt_name <> 'uuid'
)
THEN
ALTER TABLE public.hc_settlement_items
ALTER COLUMN settlement_id TYPE uuid USING NULLIF(settlement_id, '')::uuid;
END IF;
IF to_regclass('public.hc_payments') IS NOT NULL
AND EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema='public'
AND table_name='hc_payments'
AND column_name='settlement_id'
AND udt_name <> 'uuid'
)
THEN
ALTER TABLE public.hc_payments
ALTER COLUMN settlement_id TYPE uuid USING NULLIF(settlement_id, '')::uuid;
END IF;
IF to_regclass('public.hc_refunds') IS NOT NULL
AND EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema='public'
AND table_name='hc_refunds'
AND column_name='settlement_id'
AND udt_name <> 'uuid'
)
THEN
ALTER TABLE public.hc_refunds
ALTER COLUMN settlement_id TYPE uuid USING NULLIF(settlement_id, '')::uuid;
END IF;
IF to_regclass('public.hc_refunds') IS NOT NULL
AND EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema='public'
AND table_name='hc_refunds'
AND column_name='payment_id'
AND udt_name <> 'uuid'
)
THEN
ALTER TABLE public.hc_refunds
ALTER COLUMN payment_id TYPE uuid USING NULLIF(payment_id, '')::uuid;
END IF;
IF to_regclass('public.hc_ledgers') IS NOT NULL
AND EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema='public'
AND table_name='hc_ledgers'
AND column_name='settlement_id'
AND udt_name <> 'uuid'
)
THEN
ALTER TABLE public.hc_ledgers
ALTER COLUMN settlement_id TYPE uuid USING NULLIF(settlement_id, '')::uuid;
END IF;
IF to_regclass('public.hc_archive_files') IS NOT NULL
AND EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema='public'
AND table_name='hc_archive_files'
AND column_name='ledger_id'
AND udt_name <> 'uuid'
)
THEN
ALTER TABLE public.hc_archive_files
ALTER COLUMN ledger_id TYPE uuid USING NULLIF(ledger_id, '')::uuid;
END IF;
END $$;
-- ============================================================
-- Part 5: Indexes
-- ============================================================
-- hc_dispatch_assignments indexes
CREATE INDEX IF NOT EXISTS idx_hc_dispatch_assignments_wo_created
ON public.hc_dispatch_assignments (work_order_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_hc_dispatch_assignments_current
ON public.hc_dispatch_assignments (work_order_id) WHERE is_current = true;
-- Ensure each work order has at most one current assignment for distance/checkin RPC lookup.
CREATE UNIQUE INDEX IF NOT EXISTS uq_hc_dispatch_assignments_one_current
ON public.hc_dispatch_assignments (work_order_id) WHERE is_current = true;
CREATE INDEX IF NOT EXISTS idx_hc_dispatch_assignments_worker_status
ON public.hc_dispatch_assignments (worker_id, dispatch_status, created_at DESC);
-- hc_worker_locations indexes
CREATE INDEX IF NOT EXISTS idx_hc_worker_locations_wo_reported
ON public.hc_worker_locations (work_order_id, reported_at DESC);
CREATE INDEX IF NOT EXISTS idx_hc_worker_locations_worker_reported
ON public.hc_worker_locations (worker_id, reported_at DESC);
-- hc_work_order_confirmations indexes
CREATE INDEX IF NOT EXISTS idx_hc_work_order_confirmations_wo_created
ON public.hc_work_order_confirmations (work_order_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_hc_work_order_confirmations_status
ON public.hc_work_order_confirmations (status, updated_at DESC);
-- hc_evidence_files indexes
CREATE INDEX IF NOT EXISTS idx_hc_evidence_files_wo_created
ON public.hc_evidence_files (work_order_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_hc_evidence_files_upload_status
ON public.hc_evidence_files (upload_status) WHERE upload_status != 'DELETED';
-- sys_sla_config indexes
CREATE INDEX IF NOT EXISTS idx_sys_sla_config_key_scope
ON public.sys_sla_config (config_key, scope_type, scope_id, is_active) WHERE is_active = true;
-- hc_work_order_exceptions indexes
CREATE INDEX IF NOT EXISTS idx_hc_work_order_exceptions_wo_created
ON public.hc_work_order_exceptions (work_order_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_hc_work_order_exceptions_status
ON public.hc_work_order_exceptions (status, created_at DESC);
-- hc_work_order_events indexes
CREATE INDEX IF NOT EXISTS idx_hc_work_order_events_wo_created
ON public.hc_work_order_events (work_order_id, created_at DESC);
-- hc_settlements indexes
CREATE INDEX IF NOT EXISTS idx_hc_settlements_wo
ON public.hc_settlements (work_order_id);
-- ============================================================
-- Part 6: Foreign Keys
-- ============================================================
-- Make foreign key creation idempotent for reruns
ALTER TABLE public.hc_work_order_events
DROP CONSTRAINT IF EXISTS fk_hc_work_order_events_work_order,
DROP CONSTRAINT IF EXISTS fk_hc_work_order_events_worker,
DROP CONSTRAINT IF EXISTS fk_hc_work_order_events_operator;
ALTER TABLE public.hc_work_order_exceptions
DROP CONSTRAINT IF EXISTS fk_hc_work_order_exceptions_work_order,
DROP CONSTRAINT IF EXISTS fk_hc_work_order_exceptions_worker,
DROP CONSTRAINT IF EXISTS fk_hc_work_order_exceptions_created_by,
DROP CONSTRAINT IF EXISTS fk_hc_work_order_exceptions_handled_by;
ALTER TABLE public.hc_evidence_files
DROP CONSTRAINT IF EXISTS fk_hc_evidence_files_work_order,
DROP CONSTRAINT IF EXISTS fk_hc_evidence_files_uploader;
ALTER TABLE public.hc_worker_qualifications
DROP CONSTRAINT IF EXISTS fk_hc_worker_qualifications_worker,
DROP CONSTRAINT IF EXISTS fk_hc_worker_qualifications_reviewed_by;
ALTER TABLE public.hc_settlements
DROP CONSTRAINT IF EXISTS fk_hc_settlements_work_order;
ALTER TABLE public.hc_acceptances
DROP CONSTRAINT IF EXISTS fk_hc_acceptances_work_order,
DROP CONSTRAINT IF EXISTS fk_hc_acceptances_worker,
DROP CONSTRAINT IF EXISTS fk_hc_acceptances_accepted_by;
ALTER TABLE public.hc_acceptance_issues
DROP CONSTRAINT IF EXISTS fk_hc_acceptance_issues_acceptance,
DROP CONSTRAINT IF EXISTS fk_hc_acceptance_issues_work_order,
DROP CONSTRAINT IF EXISTS fk_hc_acceptance_issues_worker,
DROP CONSTRAINT IF EXISTS fk_hc_acceptance_issues_reporter,
DROP CONSTRAINT IF EXISTS fk_hc_acceptance_issues_handler;
ALTER TABLE public.hc_complaints
DROP CONSTRAINT IF EXISTS fk_hc_complaints_work_order,
DROP CONSTRAINT IF EXISTS fk_hc_complaints_acceptance_issue,
DROP CONSTRAINT IF EXISTS fk_hc_complaints_worker,
DROP CONSTRAINT IF EXISTS fk_hc_complaints_complainant,
DROP CONSTRAINT IF EXISTS fk_hc_complaints_handler;
ALTER TABLE public.hc_exception_actions
DROP CONSTRAINT IF EXISTS fk_hc_exception_actions_exception,
DROP CONSTRAINT IF EXISTS fk_hc_exception_actions_work_order,
DROP CONSTRAINT IF EXISTS fk_hc_exception_actions_worker,
DROP CONSTRAINT IF EXISTS fk_hc_exception_actions_operator;
ALTER TABLE public.hc_settlement_items
DROP CONSTRAINT IF EXISTS fk_hc_settlement_items_settlement,
DROP CONSTRAINT IF EXISTS fk_hc_settlement_items_work_order,
DROP CONSTRAINT IF EXISTS fk_hc_settlement_items_execution_record;
ALTER TABLE public.hc_payments
DROP CONSTRAINT IF EXISTS fk_hc_payments_settlement,
DROP CONSTRAINT IF EXISTS fk_hc_payments_payer;
ALTER TABLE public.hc_refunds
DROP CONSTRAINT IF EXISTS fk_hc_refunds_payment,
DROP CONSTRAINT IF EXISTS fk_hc_refunds_settlement,
DROP CONSTRAINT IF EXISTS fk_hc_refunds_created_by;
ALTER TABLE public.hc_ledgers
DROP CONSTRAINT IF EXISTS fk_hc_ledgers_settlement,
DROP CONSTRAINT IF EXISTS fk_hc_ledgers_created_by;
ALTER TABLE public.hc_archive_files
DROP CONSTRAINT IF EXISTS fk_hc_archive_files_ledger,
DROP CONSTRAINT IF EXISTS fk_hc_archive_files_created_by;
ALTER TABLE public.hc_consent_records
DROP CONSTRAINT IF EXISTS fk_hc_consent_records_elder,
DROP CONSTRAINT IF EXISTS fk_hc_consent_records_granted_by;
ALTER TABLE public.hc_dispatch_assignments
DROP CONSTRAINT IF EXISTS fk_hc_dispatch_assignments_work_order,
DROP CONSTRAINT IF EXISTS fk_hc_dispatch_assignments_worker,
DROP CONSTRAINT IF EXISTS fk_hc_dispatch_assignments_created_by,
DROP CONSTRAINT IF EXISTS fk_hc_dispatch_assignments_updated_by;
ALTER TABLE public.hc_worker_locations
DROP CONSTRAINT IF EXISTS fk_hc_worker_locations_work_order,
DROP CONSTRAINT IF EXISTS fk_hc_worker_locations_worker;
ALTER TABLE public.hc_work_order_confirmations
DROP CONSTRAINT IF EXISTS fk_hc_work_order_confirmations_work_order,
DROP CONSTRAINT IF EXISTS fk_hc_work_order_confirmations_confirmed_by;
ALTER TABLE public.hc_state_transitions
DROP CONSTRAINT IF EXISTS fk_hc_state_transitions_work_order,
DROP CONSTRAINT IF EXISTS fk_hc_state_transitions_triggered_by;
ALTER TABLE public.hc_audit_logs
DROP CONSTRAINT IF EXISTS fk_hc_audit_logs_work_order,
DROP CONSTRAINT IF EXISTS fk_hc_audit_logs_actor;
ALTER TABLE public.hc_sensitive_access_logs
DROP CONSTRAINT IF EXISTS fk_hc_sensitive_access_logs_accessor;
-- hc_work_order_events foreign keys
ALTER TABLE public.hc_work_order_events
ADD CONSTRAINT fk_hc_work_order_events_work_order
FOREIGN KEY (work_order_id) REFERENCES public.ec_care_tasks(id) ON DELETE RESTRICT,
ADD CONSTRAINT fk_hc_work_order_events_worker
FOREIGN KEY (worker_id) REFERENCES public.ak_users(id) ON DELETE SET NULL,
ADD CONSTRAINT fk_hc_work_order_events_operator
FOREIGN KEY (operator_id) REFERENCES public.ak_users(id) ON DELETE RESTRICT;
-- hc_work_order_exceptions foreign keys
ALTER TABLE public.hc_work_order_exceptions
ADD CONSTRAINT fk_hc_work_order_exceptions_work_order
FOREIGN KEY (work_order_id) REFERENCES public.ec_care_tasks(id) ON DELETE RESTRICT,
ADD CONSTRAINT fk_hc_work_order_exceptions_worker
FOREIGN KEY (worker_id) REFERENCES public.ak_users(id) ON DELETE SET NULL,
ADD CONSTRAINT fk_hc_work_order_exceptions_created_by
FOREIGN KEY (created_by) REFERENCES public.ak_users(id) ON DELETE RESTRICT,
ADD CONSTRAINT fk_hc_work_order_exceptions_handled_by
FOREIGN KEY (handled_by) REFERENCES public.ak_users(id) ON DELETE SET NULL;
-- hc_evidence_files foreign keys
ALTER TABLE public.hc_evidence_files
ADD CONSTRAINT fk_hc_evidence_files_work_order
FOREIGN KEY (work_order_id) REFERENCES public.ec_care_tasks(id) ON DELETE RESTRICT,
ADD CONSTRAINT fk_hc_evidence_files_uploader
FOREIGN KEY (uploader_id) REFERENCES public.ak_users(id) ON DELETE RESTRICT;
-- hc_worker_qualifications foreign keys
ALTER TABLE public.hc_worker_qualifications
ADD CONSTRAINT fk_hc_worker_qualifications_worker
FOREIGN KEY (worker_id) REFERENCES public.ak_users(id) ON DELETE RESTRICT,
ADD CONSTRAINT fk_hc_worker_qualifications_reviewed_by
FOREIGN KEY (reviewed_by) REFERENCES public.ak_users(id) ON DELETE SET NULL;
-- hc_settlements foreign keys
ALTER TABLE public.hc_settlements
ADD CONSTRAINT fk_hc_settlements_work_order
FOREIGN KEY (work_order_id) REFERENCES public.ec_care_tasks(id) ON DELETE RESTRICT;
-- hc_acceptances foreign keys
ALTER TABLE public.hc_acceptances
ADD CONSTRAINT fk_hc_acceptances_work_order
FOREIGN KEY (work_order_id) REFERENCES public.ec_care_tasks(id) ON DELETE RESTRICT,
ADD CONSTRAINT fk_hc_acceptances_worker
FOREIGN KEY (worker_id) REFERENCES public.ak_users(id) ON DELETE SET NULL,
ADD CONSTRAINT fk_hc_acceptances_accepted_by
FOREIGN KEY (accepted_by) REFERENCES public.ak_users(id) ON DELETE SET NULL;
-- hc_acceptance_issues foreign keys
ALTER TABLE public.hc_acceptance_issues
ADD CONSTRAINT fk_hc_acceptance_issues_acceptance
FOREIGN KEY (acceptance_id) REFERENCES public.hc_acceptances(id) ON DELETE RESTRICT,
ADD CONSTRAINT fk_hc_acceptance_issues_work_order
FOREIGN KEY (work_order_id) REFERENCES public.ec_care_tasks(id) ON DELETE RESTRICT,
ADD CONSTRAINT fk_hc_acceptance_issues_worker
FOREIGN KEY (worker_id) REFERENCES public.ak_users(id) ON DELETE SET NULL,
ADD CONSTRAINT fk_hc_acceptance_issues_reporter
FOREIGN KEY (reporter_id) REFERENCES public.ak_users(id) ON DELETE RESTRICT,
ADD CONSTRAINT fk_hc_acceptance_issues_handler
FOREIGN KEY (handler_id) REFERENCES public.ak_users(id) ON DELETE SET NULL;
-- hc_complaints foreign keys
ALTER TABLE public.hc_complaints
ADD CONSTRAINT fk_hc_complaints_work_order
FOREIGN KEY (work_order_id) REFERENCES public.ec_care_tasks(id) ON DELETE SET NULL,
ADD CONSTRAINT fk_hc_complaints_acceptance_issue
FOREIGN KEY (acceptance_issue_id) REFERENCES public.hc_acceptance_issues(id) ON DELETE SET NULL,
ADD CONSTRAINT fk_hc_complaints_worker
FOREIGN KEY (worker_id) REFERENCES public.ak_users(id) ON DELETE SET NULL,
ADD CONSTRAINT fk_hc_complaints_complainant
FOREIGN KEY (complainant_id) REFERENCES public.ak_users(id) ON DELETE RESTRICT,
ADD CONSTRAINT fk_hc_complaints_handler
FOREIGN KEY (handler_id) REFERENCES public.ak_users(id) ON DELETE SET NULL;
-- hc_exception_actions foreign keys
ALTER TABLE public.hc_exception_actions
ADD CONSTRAINT fk_hc_exception_actions_exception
FOREIGN KEY (exception_id) REFERENCES public.hc_work_order_exceptions(id) ON DELETE RESTRICT,
ADD CONSTRAINT fk_hc_exception_actions_work_order
FOREIGN KEY (work_order_id) REFERENCES public.ec_care_tasks(id) ON DELETE RESTRICT,
ADD CONSTRAINT fk_hc_exception_actions_worker
FOREIGN KEY (worker_id) REFERENCES public.ak_users(id) ON DELETE SET NULL,
ADD CONSTRAINT fk_hc_exception_actions_operator
FOREIGN KEY (operator_id) REFERENCES public.ak_users(id) ON DELETE RESTRICT;
-- hc_settlement_items foreign keys
ALTER TABLE public.hc_settlement_items
ADD CONSTRAINT fk_hc_settlement_items_settlement
FOREIGN KEY (settlement_id) REFERENCES public.hc_settlements(id) ON DELETE RESTRICT,
ADD CONSTRAINT fk_hc_settlement_items_work_order
FOREIGN KEY (work_order_id) REFERENCES public.ec_care_tasks(id) ON DELETE RESTRICT,
ADD CONSTRAINT fk_hc_settlement_items_execution_record
FOREIGN KEY (execution_record_id) REFERENCES public.ec_care_records(id) ON DELETE SET NULL;
-- hc_payments foreign keys
ALTER TABLE public.hc_payments
ADD CONSTRAINT fk_hc_payments_settlement
FOREIGN KEY (settlement_id) REFERENCES public.hc_settlements(id) ON DELETE RESTRICT,
ADD CONSTRAINT fk_hc_payments_payer
FOREIGN KEY (payer_id) REFERENCES public.ak_users(id) ON DELETE SET NULL;
-- hc_refunds foreign keys
ALTER TABLE public.hc_refunds
ADD CONSTRAINT fk_hc_refunds_payment
FOREIGN KEY (payment_id) REFERENCES public.hc_payments(id) ON DELETE RESTRICT,
ADD CONSTRAINT fk_hc_refunds_settlement
FOREIGN KEY (settlement_id) REFERENCES public.hc_settlements(id) ON DELETE RESTRICT,
ADD CONSTRAINT fk_hc_refunds_created_by
FOREIGN KEY (created_by) REFERENCES public.ak_users(id) ON DELETE SET NULL;
-- hc_ledgers foreign keys
ALTER TABLE public.hc_ledgers
ADD CONSTRAINT fk_hc_ledgers_settlement
FOREIGN KEY (settlement_id) REFERENCES public.hc_settlements(id) ON DELETE RESTRICT,
ADD CONSTRAINT fk_hc_ledgers_created_by
FOREIGN KEY (created_by) REFERENCES public.ak_users(id) ON DELETE SET NULL;
-- hc_archive_files foreign keys
ALTER TABLE public.hc_archive_files
ADD CONSTRAINT fk_hc_archive_files_ledger
FOREIGN KEY (ledger_id) REFERENCES public.hc_ledgers(id) ON DELETE RESTRICT,
ADD CONSTRAINT fk_hc_archive_files_created_by
FOREIGN KEY (created_by) REFERENCES public.ak_users(id) ON DELETE SET NULL;
-- hc_consent_records foreign keys
ALTER TABLE public.hc_consent_records
ADD CONSTRAINT fk_hc_consent_records_elder
FOREIGN KEY (elder_id) REFERENCES public.ec_elders(id) ON DELETE RESTRICT,
ADD CONSTRAINT fk_hc_consent_records_granted_by
FOREIGN KEY (granted_by) REFERENCES public.ak_users(id) ON DELETE SET NULL;
-- Location distance phase 1 foreign keys
ALTER TABLE public.hc_dispatch_assignments
ADD CONSTRAINT fk_hc_dispatch_assignments_work_order
FOREIGN KEY (work_order_id) REFERENCES public.ec_care_tasks(id) ON DELETE RESTRICT,
ADD CONSTRAINT fk_hc_dispatch_assignments_worker
FOREIGN KEY (worker_id) REFERENCES public.ak_users(id) ON DELETE SET NULL,
ADD CONSTRAINT fk_hc_dispatch_assignments_created_by
FOREIGN KEY (created_by) REFERENCES public.ak_users(id) ON DELETE SET NULL,
ADD CONSTRAINT fk_hc_dispatch_assignments_updated_by
FOREIGN KEY (updated_by) REFERENCES public.ak_users(id) ON DELETE SET NULL;
ALTER TABLE public.hc_worker_locations
ADD CONSTRAINT fk_hc_worker_locations_work_order
FOREIGN KEY (work_order_id) REFERENCES public.ec_care_tasks(id) ON DELETE RESTRICT,
ADD CONSTRAINT fk_hc_worker_locations_worker
FOREIGN KEY (worker_id) REFERENCES public.ak_users(id) ON DELETE RESTRICT;
ALTER TABLE public.hc_work_order_confirmations
ADD CONSTRAINT fk_hc_work_order_confirmations_work_order
FOREIGN KEY (work_order_id) REFERENCES public.ec_care_tasks(id) ON DELETE RESTRICT,
ADD CONSTRAINT fk_hc_work_order_confirmations_confirmed_by
FOREIGN KEY (confirmed_by) REFERENCES public.ak_users(id) ON DELETE SET NULL;
-- Audit extension tables foreign keys
ALTER TABLE public.hc_state_transitions
ADD CONSTRAINT fk_hc_state_transitions_work_order
FOREIGN KEY (work_order_id) REFERENCES public.ec_care_tasks(id) ON DELETE RESTRICT,
ADD CONSTRAINT fk_hc_state_transitions_triggered_by
FOREIGN KEY (triggered_by) REFERENCES public.ak_users(id) ON DELETE RESTRICT;
ALTER TABLE public.hc_audit_logs
ADD CONSTRAINT fk_hc_audit_logs_work_order
FOREIGN KEY (work_order_id) REFERENCES public.ec_care_tasks(id) ON DELETE SET NULL,
ADD CONSTRAINT fk_hc_audit_logs_actor
FOREIGN KEY (actor_id) REFERENCES public.ak_users(id) ON DELETE RESTRICT;
ALTER TABLE public.hc_sensitive_access_logs
ADD CONSTRAINT fk_hc_sensitive_access_logs_accessor
FOREIGN KEY (accessor_id) REFERENCES public.ak_users(id) ON DELETE RESTRICT;
-- ============================================================
-- Part 7: Seed Data - SLA Config
-- ============================================================
-- Phase 1 SLA configuration
-- Checkin radius: 50 meters (phase 1 final)
INSERT INTO public.sys_sla_config (id, config_key, config_value, value_type, scope_type, description)
VALUES
('sla_001', 'HOMECARE_CHECKIN_RADIUS_METERS', '50', 'integer', 'GLOBAL', '签到半径一期50米'),
('sla_002', 'HOMECARE_LOCATION_REFRESH_SECONDS', '30', 'integer', 'GLOBAL', '定位刷新间隔(秒)'),
('sla_003', 'HOMECARE_CHECKIN_MAX_PHOTO_COUNT', '3', 'integer', 'GLOBAL', '签到最大照片数'),
('sla_004', 'HOMECARE_CHECKIN_MAX_PHOTO_SIZE_MB', '5', 'integer', 'GLOBAL', '单张最大照片大小(MB)')
ON CONFLICT (id) DO NOTHING;
-- ============================================================
-- Part 8: Comments
-- ============================================================
COMMENT ON TABLE public.hc_work_order_events IS '居家服务工单事件表 - 业务时间线展示';
COMMENT ON TABLE public.hc_work_order_exceptions IS '居家服务工单异常表';
COMMENT ON TABLE public.hc_evidence_files IS '居家服务证据文件表 - 签到照片、异常图片等';
COMMENT ON TABLE public.hc_worker_qualifications IS '居家服务人员资质表';
COMMENT ON TABLE public.hc_settlements IS '居家服务结算表';
COMMENT ON TABLE public.hc_acceptances IS '居家服务确认表';
COMMENT ON TABLE public.hc_acceptance_issues IS '居家服务确认问题表';
COMMENT ON TABLE public.hc_complaints IS '居家服务投诉表';
COMMENT ON TABLE public.hc_exception_actions IS '居家服务异常操作表';
COMMENT ON TABLE public.hc_settlement_items IS '居家服务结算明细表';
COMMENT ON TABLE public.hc_payments IS '居家服务支付表';
COMMENT ON TABLE public.hc_refunds IS '居家服务退款表';
COMMENT ON TABLE public.hc_ledgers IS '居家服务账簿表';
COMMENT ON TABLE public.hc_archive_files IS '居家服务归档文件表';
COMMENT ON TABLE public.hc_consent_records IS '居家服务同意记录表';
COMMENT ON TABLE public.hc_dispatch_assignments IS '居家服务派单记录表 - 定位距离一期';
COMMENT ON TABLE public.hc_worker_locations IS '居家服务人员定位表 - 定位距离一期';
COMMENT ON TABLE public.hc_work_order_confirmations IS '居家服务工单确认表 - 定位距离一期';
COMMENT ON TABLE public.sys_sla_config IS '居家服务SLA配置表';
COMMENT ON TABLE public.hc_state_transitions IS '居家服务状态变迁表 - 审计';
COMMENT ON TABLE public.hc_audit_logs IS '居家服务操作审计表';
COMMENT ON TABLE public.hc_sensitive_access_logs IS '居家服务敏感访问日志表';
COMMENT ON COLUMN public.hc_evidence_files.upload_status IS '上传状态: TEMP/READY/BOUND/LOCKED/DELETED';
COMMENT ON COLUMN public.hc_evidence_files.bound_action IS '绑定动作: CHECKIN/CHECKOUT/EXCEPTION';
COMMENT ON COLUMN public.hc_worker_locations.location_scene IS '定位场景: ON_THE_WAY/CHECKIN_PRECHECK/CHECKIN/CHECKOUT';
COMMENT ON COLUMN public.hc_work_order_confirmations.confirmation_type IS '确认类型: ARRIVAL二期扩展CHECKOUT';
COMMENT ON COLUMN public.hc_work_order_confirmations.signature_url IS '二期电子签名图片URL预留';
COMMENT ON COLUMN public.hc_work_order_confirmations.signature_hash IS '二期电子签名哈希(预留)';
COMMIT;

File diff suppressed because it is too large Load Diff

View File

@@ -137,7 +137,29 @@
</view>
<view class="my-services service-card">
<scroll-view class="service-scroll" direction="horizontal" scroll-x="true" :scroll-y="false" :show-scrollbar="false" scroll-with-animation>
<view class="service-scroll-wrap">
<!-- #ifdef APP -->
<scroll-view
class="service-scroll"
direction="horizontal"
scroll-x="true"
:scroll-y="false"
:show-scrollbar="false"
scroll-with-animation
@scroll="onServiceScroll"
>
<!-- #endif -->
<!-- #ifdef H5 || MP -->
<scroll-view
class="service-scroll"
direction="horizontal"
scroll-x="true"
:scroll-y="false"
:show-scrollbar="false"
scroll-with-animation
@scroll="onServiceScroll"
>
<!-- #endif -->
<view class="service-row">
<view class="service-item" @click="goToMessages">
<view class="service-icon-wrap">
@@ -153,11 +175,12 @@
</view>
<text class="service-name">优惠券</text>
</view>
<view class="service-item" @click="goToAddress">
<view class="service-item" @click="goToFollowedShops">
<view class="service-icon-wrap">
<text class="service-text-icon">址</text>
<image class="service-icon-img" src="/static/consumer/store.png" mode="aspectFit" @error="shopIconError = true" v-if="!shopIconError" />
<text v-if="shopIconError" class="service-text-icon">店</text>
</view>
<text class="service-name">收货地址</text>
<text class="service-name">店铺关注</text>
</view>
<view class="service-item" @click="goToFavorites">
<view class="service-icon-wrap">
@@ -184,15 +207,28 @@
</view>
<text class="service-name">我的评价</text>
</view>
<view class="service-item" @click="goToFollowedShops">
<view class="service-item" @click="goToAddress">
<view class="service-icon-wrap">
<image class="service-icon-img" src="/static/consumer/store.png" mode="aspectFit" />
<text class="service-text-icon">址</text>
</view>
<text class="service-name">关注店铺</text>
<text class="service-name">收货地址</text>
</view>
<view class="service-row-end-spacer"></view>
</view>
<!-- #ifdef APP -->
</scroll-view>
<!-- #endif -->
<!-- #ifdef H5 || MP -->
</scroll-view>
<!-- #endif -->
<view class="service-right-fade"></view>
</view>
<view class="service-scroll-indicator">
<view class="service-scroll-track">
<view class="service-scroll-thumb" :style="getServiceThumbStyle()"></view>
</view>
</view>
</view>
<GuessYouLike
@@ -342,6 +378,9 @@ export default {
pageWindowHeight: 0,
isLoggedIn: false,
guessLoadMoreKey: 0,
serviceScrollLeft: 0,
serviceScrollMax: 160,
shopIconError: false,
statusBarHeight: 0,
isAndroidApp: false,
capsuleTop: 0,
@@ -582,6 +621,28 @@ export default {
return
},
onServiceScroll(event: any) {
const detailObj = this.toRecommendScrollJson(event.detail)
if (detailObj == null) {
return
}
const scrollLeft = this.readRecommendScrollMetric(detailObj, 'scrollLeft')
this.serviceScrollLeft = scrollLeft
},
getServiceThumbStyle(): string {
const ratio = this.serviceScrollLeft / this.serviceScrollMax
const maxTranslate = 20
let translateX = ratio * maxTranslate
if (translateX < 0) {
translateX = 0
}
if (translateX > maxTranslate) {
translateX = maxTranslate
}
return 'transform: translateX(' + translateX + 'px);'
},
handleGuessProductClick(productId: string) {
if (productId == null || productId === '') {
return
@@ -2316,13 +2377,63 @@ export default {
.service-card {
margin: 12px 14px 0 14px;
padding: 14px 0 14px;
padding: 14px 0 10px;
border-radius: 18px;
background-color: #ffffff;
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.04);
overflow: hidden;
}
.service-scroll-wrap {
position: relative;
width: 100%;
}
.service-scroll {
width: 100%;
box-sizing: border-box;
white-space: nowrap;
position: relative;
z-index: 2;
height: 60px;
}
.service-right-fade {
position: absolute;
right: 0;
top: 0;
bottom: 0;
width: 24px;
pointer-events: none;
z-index: 1;
background-image: linear-gradient(to right, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1));
}
.service-scroll-indicator {
height: 10px;
margin-top: 8px;
display: flex;
justify-content: center;
align-items: center;
}
.service-scroll-track {
width: 38px;
height: 4px;
border-radius: 4px;
background-color: #e5e5e5;
overflow: hidden;
}
.service-scroll-thumb {
width: 18px;
height: 4px;
border-radius: 4px;
background-color: #e4393c;
transition-property: transform;
transition-duration: 120ms;
}
.service-header {
padding-left: 16px;
padding-right: 16px;
@@ -2339,12 +2450,6 @@ export default {
color: #222222;
}
.service-scroll {
width: 100%;
box-sizing: border-box;
white-space: nowrap;
}
.service-row {
display: inline-flex;
flex-direction: row;
@@ -2352,6 +2457,7 @@ export default {
align-items: flex-start;
padding-left: 12px;
padding-right: 18px;
width: 640px;
}
.service-item {

View File

@@ -2,14 +2,7 @@
<template>
<view class="favorites-page">
<view class="favorites-nav">
<view class="nav-left" @click="handleBack">
<text class="back-icon"></text>
</view>
<view v-if="!isSearchMode" class="nav-center">
<text class="nav-title">我的收藏</text>
</view>
<view v-else class="nav-search-panel">
<view class="nav-search-panel">
<view class="search-input-wrap">
<view class="search-icon">
<view class="search-icon-circle"></view>

View File

@@ -3281,6 +3281,89 @@ class SupabaseService {
}
}
// 标记单条通知为已读 (ml_notifications)
async markUserNotificationRead(notificationId: string): Promise<boolean> {
try {
const res = await supa
.from('ml_notifications')
.update({ is_read: true })
.eq('id', notificationId)
.execute()
return res.error == null
} catch (e) {
console.error('[markUserNotificationRead] 标记通知已读失败:', e)
return false
}
}
// 标记聊天会话为已读
async markChatRoomRead(merchantId: string): Promise<boolean> {
try {
const userId = this.getCurrentUserId()
if (userId == null) return false
const res = await supa
.from('ml_chat_rooms')
.update({ unread_count: 0 })
.eq('user_id', userId)
.eq('merchant_id', merchantId)
.execute()
return res.error == null
} catch (e) {
console.error('[markChatRoomRead] 标记会话已读失败:', e)
return false
}
}
// 一键已读:所有通知 + 所有会话
async markAllMessagesRead(): Promise<boolean> {
try {
const userId = this.getCurrentUserId()
if (userId == null) return false
const noteRes = await supa
.from('ml_notifications')
.update({ is_read: true })
.eq('user_id', userId)
.eq('is_read', false)
.execute()
if (noteRes.error != null) {
console.error('[markAllMessagesRead] 通知已读失败:', noteRes.error)
}
const roomRes = await supa
.from('ml_chat_rooms')
.update({ unread_count: 0 })
.eq('user_id', userId)
.gt('unread_count', 0)
.execute()
if (roomRes.error != null) {
console.error('[markAllMessagesRead] 会话已读失败:', roomRes.error)
}
return true
} catch (e) {
console.error('[markAllMessagesRead] 一键已读失败:', e)
return false
}
}
// 软删除消息(前端兜底:后端表缺少软删除字段时仅 console.warn
async softDeleteMessages(messageIds: Array<string>): Promise<boolean> {
try {
const userId = this.getCurrentUserId()
if (userId == null) return false
if (messageIds.length === 0) return true
// TODO: 后端 ml_notifications / ml_chat_rooms 表当前缺少 consumer_deleted_at / deleted_at / user_deleted_at 字段
// 现阶段不执行数据库删除/更新,仅打印警告。如需持久化软删除,请先在相关表增加对应字段并补充 API。
console.warn('[softDeleteMessages] 后端缺少软删除字段/API本次跳过数据库操作。messageIds:', messageIds)
return true
} catch (e) {
console.error('[softDeleteMessages] 软删除失败:', e)
return false
}
}
// 获取用户通知 (系统、活动、订单)
async getUserNotifications(type: string | null = null): Promise<Notification[]> {
try {

View File

@@ -1,119 +1,640 @@
[自动热重载] 已开启代码文件保存后自动热重载
mp.esm.js:529 [getUserNotifications] 开始获取通知
mp.esm.js:529 [ak-req] request GET auth-mode: pre-set prefer: (none)
uni.api.esm.js:1042 GET http://119.146.131.237:9126/rest/v1/ml_user_coupons?select=*%2C%20template%3Aml_coupon_templates(name%2C%20amount%2C%20min_spend)&order=expire_at.asc&user_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c&status=eq.1 400 (Bad Request)(env: Windows,mp,1.06.2504030; lib: 3.15.2)
(anonymous) @ uni.api.esm.js:1042
invokeApi @ uni.api.esm.js:330
promiseApi @ uni.api.esm.js:889
(anonymous) @ ak-req.uts:207
doOnce @ ak-req.uts:206
_loop$ @ ak-req.uts:320
s @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
s @ regeneratorRuntime.js?forceSync=true:1
_ @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
fulfilled @ uni.mp.esm.js:1134
Promise.then (async)
step @ uni.mp.esm.js:1134
(anonymous) @ uni.mp.esm.js:1134
__awaiter @ uni.mp.esm.js:1134
request @ ak-req.uts:148
_callee19$ @ aksupa.uts:1288
s @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ uni.mp.esm.js:1134
__awaiter @ uni.mp.esm.js:1134
requestWithAutoRefresh @ aksupa.uts:1287
_callee11$ @ aksupa.uts:1069
s @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ uni.mp.esm.js:1134
__awaiter @ uni.mp.esm.js:1134
select @ aksupa.uts:984
_callee$ @ aksupa.uts:388
s @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ uni.mp.esm.js:1134
__awaiter @ uni.mp.esm.js:1134
execute @ aksupa.uts:368
_callee109$ @ supabaseService.uts:7726
s @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ uni.mp.esm.js:1134
__awaiter @ uni.mp.esm.js:1134
getUserCoupons @ supabaseService.uts:7709
_callee$ @ coupons.uvue:41
s @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ uni.mp.esm.js:1134
__awaiter @ uni.mp.esm.js:1134
loadCoupons @ coupons.uvue:38
(anonymous) @ coupons.uvue:67
(anonymous) @ vue.runtime.esm.js:2483
mp.esm.js:529 [getUserNotifications] 获取到通知数量: 3
mp.esm.js:529 [getChatRooms] 开始获取聊天会话
mp.esm.js:529 [ak-req] request GET auth-mode: pre-set prefer: (none)
mp.esm.js:529 [getChatRooms] 获取到会话数量: 2
mp.esm.js:529 [Vue warn]: Property "manageMode" was accessed during render but is not defined on instance.
at <Messages >
(anonymous) @ mp.esm.js:529
warn$1 @ vue.runtime.esm.js:1251
get @ vue.runtime.esm.js:2629
(anonymous) @ messages.uvue:760
vFor @ vue.runtime.esm.js:6364
f @ vue.runtime.esm.js:6718
(anonymous) @ messages.uvue:760
renderComponentRoot @ vue.runtime.esm.js:5065
componentUpdateFn @ vue.runtime.esm.js:5192
run @ vue.runtime.esm.js:180
instance.update @ vue.runtime.esm.js:5216
callWithErrorHandling @ vue.runtime.esm.js:1356
callWithAsyncErrorHandling @ vue.runtime.esm.js:1363
hook.__weh.hook.__weh @ vue.runtime.esm.js:2461
invokeArrayFns @ uni-shared.es.js:1344
callHook @ uni.mp.esm.js:241
ready @ uni.mp.esm.js:1039
Show 31 more frames
mp.esm.js:529 [ak-req] HTTP error response(env: Windows,mp,1.06.2504030; lib: 3.15.2)
(anonymous) @ mp.esm.js:529
__f__ @ uni.api.esm.js:590
success @ ak-req.uts:297
(anonymous) @ uni.api.esm.js:946
mp.esm.js:529 [ak-req] status: 400(env: Windows,mp,1.06.2504030; lib: 3.15.2)
(anonymous) @ mp.esm.js:529
__f__ @ uni.api.esm.js:590
success @ ak-req.uts:298
(anonymous) @ uni.api.esm.js:946
mp.esm.js:529 [ak-req] body: {"code":"42703","details":null,"hint":null,"message":"column ml_coupon_templates_1.amount does not exist"}(env: Windows,mp,1.06.2504030; lib: 3.15.2)
(anonymous) @ mp.esm.js:529
__f__ @ uni.api.esm.js:590
success @ ak-req.uts:299
(anonymous) @ uni.api.esm.js:946
mp.esm.js:529 获取优惠券失败: UniError: 请求失败: 400
at _construct (weapp:///http://127.0.0.1:54597/appservice/@babel/runtime/helpers/construct.js?forceSync=true:1:1227)
at new r (weapp:///http://127.0.0.1:54597/appservice/@babel/runtime/helpers/wrapNativeSuper.js?forceSync=true:1:1357)
at UniError2.<anonymous> (weapp:///http://127.0.0.1:54597/appservice/@babel/runtime/helpers/createSuper.js?forceSync=true:1:1176)
at new UniError2 (weapp:///http://127.0.0.1:54597/appservice/common/vendor.js?t=wechat&s=1780447358541&v=9f592ae44ab8ade1f25e60a1b13f3b53:890:22)
at Object.toUniError (weapp:///http://127.0.0.1:54597/appservice/utils/utils.js?t=wechat&s=1780447358541&v=3d196abbae2c5ba600f3f4f5ac011322:77:18)
at AkSupa._callee19$ (weapp:///http://127.0.0.1:54597/appservice/components/supadb/aksupa.js?t=wechat&s=1780447358541&v=115b88a2780d4bcec72cc60e543be136:1959:41)
at s (weapp:///http://127.0.0.1:54597/appservice/@babel/runtime/helpers/regeneratorRuntime.js?forceSync=true:1:1588)
at Generator.<anonymous> (weapp:///http://127.0.0.1:54597/appservice/@babel/runtime/helpers/regeneratorRuntime.js?forceSync=true:1:2925)
at Generator.next (weapp:///http://127.0.0.1:54597/appservice/@babel/runtime/helpers/regeneratorRuntime.js?forceSync=true:1:1951)
at fulfilled (weapp:///http://127.0.0.1:54597/appservice/common/vendor.js?t=wechat&s=1780447358541&v=9f592ae44ab8ade1f25e60a1b13f3b53:10009:24)(env: Windows,mp,1.06.2504030; lib: 3.15.2)
(anonymous) @ mp.esm.js:529
__f__ @ uni.api.esm.js:590
_callee109$ @ supabaseService.uts:7729
flushJobs @ vue.runtime.esm.js:1563
Promise.then (async)
queueFlush @ vue.runtime.esm.js:1472
queueJob @ vue.runtime.esm.js:1466
(anonymous) @ vue.runtime.esm.js:5210
resetScheduling @ vue.runtime.esm.js:263
triggerEffects @ vue.runtime.esm.js:307
triggerRefValue @ vue.runtime.esm.js:1067
set @ vue.runtime.esm.js:1112
_callee$ @ messages.uvue:639
s @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
fulfilled @ uni.mp.esm.js:1134
Promise.then (async)
step @ uni.mp.esm.js:1134
fulfilled @ uni.mp.esm.js:1134
Promise.then (async)
step @ uni.mp.esm.js:1134
(anonymous) @ uni.mp.esm.js:1134
__awaiter @ uni.mp.esm.js:1134
getUserCoupons @ supabaseService.uts:7709
_callee$ @ coupons.uvue:41
s @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ uni.mp.esm.js:1134
__awaiter @ uni.mp.esm.js:1134
loadCoupons @ coupons.uvue:38
(anonymous) @ coupons.uvue:67
(anonymous) @ vue.runtime.esm.js:2483
loadMessages @ messages.uvue:416
(anonymous) @ messages.uvue:759
callWithErrorHandling @ vue.runtime.esm.js:1356
callWithAsyncErrorHandling @ vue.runtime.esm.js:1363
hook.__weh.hook.__weh @ vue.runtime.esm.js:2461
invokeArrayFns @ uni-shared.es.js:1344
callHook @ uni.mp.esm.js:241
ready @ uni.mp.esm.js:1039
mpOptions.<computed> @ uni.mp.esm.js:281
mp.esm.js:529 [Vue warn]: Property "manageMode" was accessed during render but is not defined on instance.
at <Messages >
(anonymous) @ mp.esm.js:529
warn$1 @ vue.runtime.esm.js:1251
get @ vue.runtime.esm.js:2629
(anonymous) @ messages.uvue:760
vFor @ vue.runtime.esm.js:6364
f @ vue.runtime.esm.js:6718
(anonymous) @ messages.uvue:760
renderComponentRoot @ vue.runtime.esm.js:5065
componentUpdateFn @ vue.runtime.esm.js:5192
run @ vue.runtime.esm.js:180
instance.update @ vue.runtime.esm.js:5216
callWithErrorHandling @ vue.runtime.esm.js:1356
flushJobs @ vue.runtime.esm.js:1563
Promise.then (async)
queueFlush @ vue.runtime.esm.js:1472
queueJob @ vue.runtime.esm.js:1466
(anonymous) @ vue.runtime.esm.js:5210
resetScheduling @ vue.runtime.esm.js:263
triggerEffects @ vue.runtime.esm.js:307
triggerRefValue @ vue.runtime.esm.js:1067
set @ vue.runtime.esm.js:1112
_callee$ @ messages.uvue:639
s @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
fulfilled @ uni.mp.esm.js:1134
Promise.then (async)
step @ uni.mp.esm.js:1134
fulfilled @ uni.mp.esm.js:1134
Promise.then (async)
step @ uni.mp.esm.js:1134
(anonymous) @ uni.mp.esm.js:1134
__awaiter @ uni.mp.esm.js:1134
loadMessages @ messages.uvue:416
(anonymous) @ messages.uvue:759
callWithErrorHandling @ vue.runtime.esm.js:1356
callWithAsyncErrorHandling @ vue.runtime.esm.js:1363
hook.__weh.hook.__weh @ vue.runtime.esm.js:2461
invokeArrayFns @ uni-shared.es.js:1344
callHook @ uni.mp.esm.js:241
mpOptions.<computed> @ uni.mp.esm.js:281
mp.esm.js:529 [Vue warn]: Property "manageMode" was accessed during render but is not defined on instance.
at <Messages >
(anonymous) @ mp.esm.js:529
warn$1 @ vue.runtime.esm.js:1251
get @ vue.runtime.esm.js:2629
(anonymous) @ messages.uvue:760
vFor @ vue.runtime.esm.js:6364
f @ vue.runtime.esm.js:6718
(anonymous) @ messages.uvue:760
renderComponentRoot @ vue.runtime.esm.js:5065
componentUpdateFn @ vue.runtime.esm.js:5192
run @ vue.runtime.esm.js:180
instance.update @ vue.runtime.esm.js:5216
callWithErrorHandling @ vue.runtime.esm.js:1356
flushJobs @ vue.runtime.esm.js:1563
Promise.then (async)
queueFlush @ vue.runtime.esm.js:1472
queueJob @ vue.runtime.esm.js:1466
(anonymous) @ vue.runtime.esm.js:5210
resetScheduling @ vue.runtime.esm.js:263
triggerEffects @ vue.runtime.esm.js:307
triggerRefValue @ vue.runtime.esm.js:1067
set @ vue.runtime.esm.js:1112
_callee$ @ messages.uvue:639
s @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
fulfilled @ uni.mp.esm.js:1134
Promise.then (async)
step @ uni.mp.esm.js:1134
fulfilled @ uni.mp.esm.js:1134
Promise.then (async)
step @ uni.mp.esm.js:1134
(anonymous) @ uni.mp.esm.js:1134
__awaiter @ uni.mp.esm.js:1134
loadMessages @ messages.uvue:416
(anonymous) @ messages.uvue:759
callWithErrorHandling @ vue.runtime.esm.js:1356
callWithAsyncErrorHandling @ vue.runtime.esm.js:1363
hook.__weh.hook.__weh @ vue.runtime.esm.js:2461
invokeArrayFns @ uni-shared.es.js:1344
callHook @ uni.mp.esm.js:241
mpOptions.<computed> @ uni.mp.esm.js:281
mp.esm.js:529 [Vue warn]: Property "manageMode" was accessed during render but is not defined on instance.
at <Messages >
(anonymous) @ mp.esm.js:529
warn$1 @ vue.runtime.esm.js:1251
get @ vue.runtime.esm.js:2629
(anonymous) @ messages.uvue:760
vFor @ vue.runtime.esm.js:6364
f @ vue.runtime.esm.js:6718
(anonymous) @ messages.uvue:760
renderComponentRoot @ vue.runtime.esm.js:5065
componentUpdateFn @ vue.runtime.esm.js:5192
run @ vue.runtime.esm.js:180
instance.update @ vue.runtime.esm.js:5216
callWithErrorHandling @ vue.runtime.esm.js:1356
flushJobs @ vue.runtime.esm.js:1563
Promise.then (async)
queueFlush @ vue.runtime.esm.js:1472
queueJob @ vue.runtime.esm.js:1466
(anonymous) @ vue.runtime.esm.js:5210
resetScheduling @ vue.runtime.esm.js:263
triggerEffects @ vue.runtime.esm.js:307
triggerRefValue @ vue.runtime.esm.js:1067
set @ vue.runtime.esm.js:1112
_callee$ @ messages.uvue:639
s @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
fulfilled @ uni.mp.esm.js:1134
Promise.then (async)
step @ uni.mp.esm.js:1134
fulfilled @ uni.mp.esm.js:1134
Promise.then (async)
step @ uni.mp.esm.js:1134
(anonymous) @ uni.mp.esm.js:1134
__awaiter @ uni.mp.esm.js:1134
loadMessages @ messages.uvue:416
(anonymous) @ messages.uvue:759
callWithErrorHandling @ vue.runtime.esm.js:1356
callWithAsyncErrorHandling @ vue.runtime.esm.js:1363
hook.__weh.hook.__weh @ vue.runtime.esm.js:2461
invokeArrayFns @ uni-shared.es.js:1344
callHook @ uni.mp.esm.js:241
mpOptions.<computed> @ uni.mp.esm.js:281
mp.esm.js:529 [Vue warn]: Property "manageMode" was accessed during render but is not defined on instance.
at <Messages >
(anonymous) @ mp.esm.js:529
warn$1 @ vue.runtime.esm.js:1251
get @ vue.runtime.esm.js:2629
(anonymous) @ messages.uvue:760
vFor @ vue.runtime.esm.js:6364
f @ vue.runtime.esm.js:6718
(anonymous) @ messages.uvue:760
renderComponentRoot @ vue.runtime.esm.js:5065
componentUpdateFn @ vue.runtime.esm.js:5192
run @ vue.runtime.esm.js:180
instance.update @ vue.runtime.esm.js:5216
callWithErrorHandling @ vue.runtime.esm.js:1356
flushJobs @ vue.runtime.esm.js:1563
Promise.then (async)
queueFlush @ vue.runtime.esm.js:1472
queueJob @ vue.runtime.esm.js:1466
(anonymous) @ vue.runtime.esm.js:5210
resetScheduling @ vue.runtime.esm.js:263
triggerEffects @ vue.runtime.esm.js:307
triggerRefValue @ vue.runtime.esm.js:1067
set @ vue.runtime.esm.js:1112
_callee$ @ messages.uvue:639
s @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
fulfilled @ uni.mp.esm.js:1134
Promise.then (async)
step @ uni.mp.esm.js:1134
fulfilled @ uni.mp.esm.js:1134
Promise.then (async)
step @ uni.mp.esm.js:1134
(anonymous) @ uni.mp.esm.js:1134
__awaiter @ uni.mp.esm.js:1134
loadMessages @ messages.uvue:416
(anonymous) @ messages.uvue:759
callWithErrorHandling @ vue.runtime.esm.js:1356
callWithAsyncErrorHandling @ vue.runtime.esm.js:1363
hook.__weh.hook.__weh @ vue.runtime.esm.js:2461
invokeArrayFns @ uni-shared.es.js:1344
callHook @ uni.mp.esm.js:241
mpOptions.<computed> @ uni.mp.esm.js:281
mp.esm.js:529 [Vue warn]: Property "manageMode" was accessed during render but is not defined on instance.
at <Messages >
(anonymous) @ mp.esm.js:529
warn$1 @ vue.runtime.esm.js:1251
get @ vue.runtime.esm.js:2629
(anonymous) @ messages.uvue:760
vFor @ vue.runtime.esm.js:6364
f @ vue.runtime.esm.js:6718
(anonymous) @ messages.uvue:760
renderComponentRoot @ vue.runtime.esm.js:5065
componentUpdateFn @ vue.runtime.esm.js:5192
run @ vue.runtime.esm.js:180
instance.update @ vue.runtime.esm.js:5216
callWithErrorHandling @ vue.runtime.esm.js:1356
flushJobs @ vue.runtime.esm.js:1563
Promise.then (async)
queueFlush @ vue.runtime.esm.js:1472
queueJob @ vue.runtime.esm.js:1466
(anonymous) @ vue.runtime.esm.js:5210
resetScheduling @ vue.runtime.esm.js:263
triggerEffects @ vue.runtime.esm.js:307
triggerRefValue @ vue.runtime.esm.js:1067
set @ vue.runtime.esm.js:1112
_callee$ @ messages.uvue:639
s @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
fulfilled @ uni.mp.esm.js:1134
Promise.then (async)
step @ uni.mp.esm.js:1134
fulfilled @ uni.mp.esm.js:1134
Promise.then (async)
step @ uni.mp.esm.js:1134
(anonymous) @ uni.mp.esm.js:1134
__awaiter @ uni.mp.esm.js:1134
loadMessages @ messages.uvue:416
(anonymous) @ messages.uvue:759
callWithErrorHandling @ vue.runtime.esm.js:1356
callWithAsyncErrorHandling @ vue.runtime.esm.js:1363
hook.__weh.hook.__weh @ vue.runtime.esm.js:2461
invokeArrayFns @ uni-shared.es.js:1344
callHook @ uni.mp.esm.js:241
mpOptions.<computed> @ uni.mp.esm.js:281
mp.esm.js:529 [Vue warn]: Property "manageMode" was accessed during render but is not defined on instance.
at <Messages >
(anonymous) @ mp.esm.js:529
warn$1 @ vue.runtime.esm.js:1251
get @ vue.runtime.esm.js:2629
(anonymous) @ messages.uvue:760
vFor @ vue.runtime.esm.js:6364
f @ vue.runtime.esm.js:6718
(anonymous) @ messages.uvue:760
renderComponentRoot @ vue.runtime.esm.js:5065
componentUpdateFn @ vue.runtime.esm.js:5192
run @ vue.runtime.esm.js:180
instance.update @ vue.runtime.esm.js:5216
callWithErrorHandling @ vue.runtime.esm.js:1356
flushJobs @ vue.runtime.esm.js:1563
Promise.then (async)
queueFlush @ vue.runtime.esm.js:1472
queueJob @ vue.runtime.esm.js:1466
(anonymous) @ vue.runtime.esm.js:5210
resetScheduling @ vue.runtime.esm.js:263
triggerEffects @ vue.runtime.esm.js:307
triggerRefValue @ vue.runtime.esm.js:1067
set @ vue.runtime.esm.js:1112
_callee$ @ messages.uvue:639
s @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
fulfilled @ uni.mp.esm.js:1134
Promise.then (async)
step @ uni.mp.esm.js:1134
fulfilled @ uni.mp.esm.js:1134
Promise.then (async)
step @ uni.mp.esm.js:1134
(anonymous) @ uni.mp.esm.js:1134
__awaiter @ uni.mp.esm.js:1134
loadMessages @ messages.uvue:416
(anonymous) @ messages.uvue:759
callWithErrorHandling @ vue.runtime.esm.js:1356
callWithAsyncErrorHandling @ vue.runtime.esm.js:1363
hook.__weh.hook.__weh @ vue.runtime.esm.js:2461
invokeArrayFns @ uni-shared.es.js:1344
callHook @ uni.mp.esm.js:241
mpOptions.<computed> @ uni.mp.esm.js:281
mp.esm.js:529 [Vue warn]: Property "manageMode" was accessed during render but is not defined on instance.
at <Messages >
(anonymous) @ mp.esm.js:529
warn$1 @ vue.runtime.esm.js:1251
get @ vue.runtime.esm.js:2629
(anonymous) @ messages.uvue:760
vFor @ vue.runtime.esm.js:6364
f @ vue.runtime.esm.js:6718
(anonymous) @ messages.uvue:760
renderComponentRoot @ vue.runtime.esm.js:5065
componentUpdateFn @ vue.runtime.esm.js:5192
run @ vue.runtime.esm.js:180
instance.update @ vue.runtime.esm.js:5216
callWithErrorHandling @ vue.runtime.esm.js:1356
flushJobs @ vue.runtime.esm.js:1563
Promise.then (async)
queueFlush @ vue.runtime.esm.js:1472
queueJob @ vue.runtime.esm.js:1466
(anonymous) @ vue.runtime.esm.js:5210
resetScheduling @ vue.runtime.esm.js:263
triggerEffects @ vue.runtime.esm.js:307
triggerRefValue @ vue.runtime.esm.js:1067
set @ vue.runtime.esm.js:1112
_callee$ @ messages.uvue:639
s @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
fulfilled @ uni.mp.esm.js:1134
Promise.then (async)
step @ uni.mp.esm.js:1134
fulfilled @ uni.mp.esm.js:1134
Promise.then (async)
step @ uni.mp.esm.js:1134
(anonymous) @ uni.mp.esm.js:1134
__awaiter @ uni.mp.esm.js:1134
loadMessages @ messages.uvue:416
(anonymous) @ messages.uvue:759
callWithErrorHandling @ vue.runtime.esm.js:1356
callWithAsyncErrorHandling @ vue.runtime.esm.js:1363
hook.__weh.hook.__weh @ vue.runtime.esm.js:2461
invokeArrayFns @ uni-shared.es.js:1344
callHook @ uni.mp.esm.js:241
mpOptions.<computed> @ uni.mp.esm.js:281
mp.esm.js:529 [Vue warn]: Property "manageMode" was accessed during render but is not defined on instance.
at <Messages >
(anonymous) @ mp.esm.js:529
warn$1 @ vue.runtime.esm.js:1251
get @ vue.runtime.esm.js:2629
(anonymous) @ messages.uvue:760
vFor @ vue.runtime.esm.js:6364
f @ vue.runtime.esm.js:6718
(anonymous) @ messages.uvue:760
renderComponentRoot @ vue.runtime.esm.js:5065
componentUpdateFn @ vue.runtime.esm.js:5192
run @ vue.runtime.esm.js:180
instance.update @ vue.runtime.esm.js:5216
callWithErrorHandling @ vue.runtime.esm.js:1356
flushJobs @ vue.runtime.esm.js:1563
Promise.then (async)
queueFlush @ vue.runtime.esm.js:1472
queueJob @ vue.runtime.esm.js:1466
(anonymous) @ vue.runtime.esm.js:5210
resetScheduling @ vue.runtime.esm.js:263
triggerEffects @ vue.runtime.esm.js:307
triggerRefValue @ vue.runtime.esm.js:1067
set @ vue.runtime.esm.js:1112
_callee$ @ messages.uvue:639
s @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
(anonymous) @ regeneratorRuntime.js?forceSync=true:1
fulfilled @ uni.mp.esm.js:1134
Promise.then (async)
step @ uni.mp.esm.js:1134
fulfilled @ uni.mp.esm.js:1134
Promise.then (async)
step @ uni.mp.esm.js:1134
(anonymous) @ uni.mp.esm.js:1134
__awaiter @ uni.mp.esm.js:1134
loadMessages @ messages.uvue:416
(anonymous) @ messages.uvue:759
callWithErrorHandling @ vue.runtime.esm.js:1356
callWithAsyncErrorHandling @ vue.runtime.esm.js:1363
hook.__weh.hook.__weh @ vue.runtime.esm.js:2461
invokeArrayFns @ uni-shared.es.js:1344
callHook @ uni.mp.esm.js:241
mpOptions.<computed> @ uni.mp.esm.js:281
[渲染层网络层错误] Failed to load local image resource /static/icons/system-notice.png
the server responded with a status of 500 (HTTP/1.1 500 Internal Server Error)
(env: Windows,mp,1.06.2504030; lib: 3.15.2)
mp.esm.js:529 [Vue warn]: Property "manageMode" was accessed during render but is not defined on instance.
at <Messages >
(anonymous) @ mp.esm.js:529
warn$1 @ vue.runtime.esm.js:1251
get @ vue.runtime.esm.js:2629
(anonymous) @ messages.uvue:760
vFor @ vue.runtime.esm.js:6364
f @ vue.runtime.esm.js:6718
(anonymous) @ messages.uvue:760
renderComponentRoot @ vue.runtime.esm.js:5065
componentUpdateFn @ vue.runtime.esm.js:5192
run @ vue.runtime.esm.js:180
instance.update @ vue.runtime.esm.js:5216
callWithErrorHandling @ vue.runtime.esm.js:1356
flushJobs @ vue.runtime.esm.js:1563
Promise.then (async)
queueFlush @ vue.runtime.esm.js:1472
queueJob @ vue.runtime.esm.js:1466
(anonymous) @ vue.runtime.esm.js:5210
resetScheduling @ vue.runtime.esm.js:263
trigger @ vue.runtime.esm.js:403
set @ vue.runtime.esm.js:524
(anonymous) @ messages.uvue:760
callWithErrorHandling @ vue.runtime.esm.js:1356
callWithAsyncErrorHandling @ vue.runtime.esm.js:1363
invoke @ vue.runtime.esm.js:6223
invoker @ vue.runtime.esm.js:6235
mp.esm.js:529 [Vue warn]: Property "manageMode" was accessed during render but is not defined on instance.
at <Messages >
(anonymous) @ mp.esm.js:529
warn$1 @ vue.runtime.esm.js:1251
get @ vue.runtime.esm.js:2629
(anonymous) @ messages.uvue:760
vFor @ vue.runtime.esm.js:6364
f @ vue.runtime.esm.js:6718
(anonymous) @ messages.uvue:760
renderComponentRoot @ vue.runtime.esm.js:5065
componentUpdateFn @ vue.runtime.esm.js:5192
run @ vue.runtime.esm.js:180
instance.update @ vue.runtime.esm.js:5216
callWithErrorHandling @ vue.runtime.esm.js:1356
flushJobs @ vue.runtime.esm.js:1563
Promise.then (async)
queueFlush @ vue.runtime.esm.js:1472
queueJob @ vue.runtime.esm.js:1466
(anonymous) @ vue.runtime.esm.js:5210
resetScheduling @ vue.runtime.esm.js:263
trigger @ vue.runtime.esm.js:403
set @ vue.runtime.esm.js:524
(anonymous) @ messages.uvue:760
callWithErrorHandling @ vue.runtime.esm.js:1356
callWithAsyncErrorHandling @ vue.runtime.esm.js:1363
invoke @ vue.runtime.esm.js:6223
invoker @ vue.runtime.esm.js:6235
mp.esm.js:529 [Vue warn]: Property "manageMode" was accessed during render but is not defined on instance.
at <Messages >
(anonymous) @ mp.esm.js:529
warn$1 @ vue.runtime.esm.js:1251
get @ vue.runtime.esm.js:2629
(anonymous) @ messages.uvue:760
vFor @ vue.runtime.esm.js:6364
f @ vue.runtime.esm.js:6718
(anonymous) @ messages.uvue:760
renderComponentRoot @ vue.runtime.esm.js:5065
componentUpdateFn @ vue.runtime.esm.js:5192
run @ vue.runtime.esm.js:180
instance.update @ vue.runtime.esm.js:5216
callWithErrorHandling @ vue.runtime.esm.js:1356
flushJobs @ vue.runtime.esm.js:1563
Promise.then (async)
queueFlush @ vue.runtime.esm.js:1472
queueJob @ vue.runtime.esm.js:1466
(anonymous) @ vue.runtime.esm.js:5210
resetScheduling @ vue.runtime.esm.js:263
trigger @ vue.runtime.esm.js:403
set @ vue.runtime.esm.js:524
(anonymous) @ messages.uvue:760
callWithErrorHandling @ vue.runtime.esm.js:1356
callWithAsyncErrorHandling @ vue.runtime.esm.js:1363
invoke @ vue.runtime.esm.js:6223
invoker @ vue.runtime.esm.js:6235
mp.esm.js:529 [Vue warn]: Property "manageMode" was accessed during render but is not defined on instance.
at <Messages >
(anonymous) @ mp.esm.js:529
warn$1 @ vue.runtime.esm.js:1251
get @ vue.runtime.esm.js:2629
(anonymous) @ messages.uvue:760
vFor @ vue.runtime.esm.js:6364
f @ vue.runtime.esm.js:6718
(anonymous) @ messages.uvue:760
renderComponentRoot @ vue.runtime.esm.js:5065
componentUpdateFn @ vue.runtime.esm.js:5192
run @ vue.runtime.esm.js:180
instance.update @ vue.runtime.esm.js:5216
callWithErrorHandling @ vue.runtime.esm.js:1356
flushJobs @ vue.runtime.esm.js:1563
Promise.then (async)
queueFlush @ vue.runtime.esm.js:1472
queueJob @ vue.runtime.esm.js:1466
(anonymous) @ vue.runtime.esm.js:5210
resetScheduling @ vue.runtime.esm.js:263
trigger @ vue.runtime.esm.js:403
set @ vue.runtime.esm.js:524
(anonymous) @ messages.uvue:760
callWithErrorHandling @ vue.runtime.esm.js:1356
callWithAsyncErrorHandling @ vue.runtime.esm.js:1363
invoke @ vue.runtime.esm.js:6223
invoker @ vue.runtime.esm.js:6235
mp.esm.js:529 [Vue warn]: Property "manageMode" was accessed during render but is not defined on instance.
at <Messages >
(anonymous) @ mp.esm.js:529
warn$1 @ vue.runtime.esm.js:1251
get @ vue.runtime.esm.js:2629
(anonymous) @ messages.uvue:760
vFor @ vue.runtime.esm.js:6364
f @ vue.runtime.esm.js:6718
(anonymous) @ messages.uvue:760
renderComponentRoot @ vue.runtime.esm.js:5065
componentUpdateFn @ vue.runtime.esm.js:5192
run @ vue.runtime.esm.js:180
instance.update @ vue.runtime.esm.js:5216
callWithErrorHandling @ vue.runtime.esm.js:1356
flushJobs @ vue.runtime.esm.js:1563
Promise.then (async)
queueFlush @ vue.runtime.esm.js:1472
queueJob @ vue.runtime.esm.js:1466
(anonymous) @ vue.runtime.esm.js:5210
resetScheduling @ vue.runtime.esm.js:263
trigger @ vue.runtime.esm.js:403
set @ vue.runtime.esm.js:524
(anonymous) @ messages.uvue:760
callWithErrorHandling @ vue.runtime.esm.js:1356
callWithAsyncErrorHandling @ vue.runtime.esm.js:1363
invoke @ vue.runtime.esm.js:6223
invoker @ vue.runtime.esm.js:6235
mp.esm.js:529 [Vue warn]: Property "manageMode" was accessed during render but is not defined on instance.
at <Messages >
(anonymous) @ mp.esm.js:529
warn$1 @ vue.runtime.esm.js:1251
get @ vue.runtime.esm.js:2629
(anonymous) @ messages.uvue:760
vFor @ vue.runtime.esm.js:6364
f @ vue.runtime.esm.js:6718
(anonymous) @ messages.uvue:760
renderComponentRoot @ vue.runtime.esm.js:5065
componentUpdateFn @ vue.runtime.esm.js:5192
run @ vue.runtime.esm.js:180
instance.update @ vue.runtime.esm.js:5216
callWithErrorHandling @ vue.runtime.esm.js:1356
flushJobs @ vue.runtime.esm.js:1563
Promise.then (async)
queueFlush @ vue.runtime.esm.js:1472
queueJob @ vue.runtime.esm.js:1466
(anonymous) @ vue.runtime.esm.js:5210
resetScheduling @ vue.runtime.esm.js:263
trigger @ vue.runtime.esm.js:403
set @ vue.runtime.esm.js:524
(anonymous) @ messages.uvue:760
callWithErrorHandling @ vue.runtime.esm.js:1356
callWithAsyncErrorHandling @ vue.runtime.esm.js:1363
invoke @ vue.runtime.esm.js:6223
invoker @ vue.runtime.esm.js:6235
mp.esm.js:529 [Vue warn]: Property "manageMode" was accessed during render but is not defined on instance.
at <Messages >
(anonymous) @ mp.esm.js:529
warn$1 @ vue.runtime.esm.js:1251
get @ vue.runtime.esm.js:2629
(anonymous) @ messages.uvue:760
vFor @ vue.runtime.esm.js:6364
f @ vue.runtime.esm.js:6718
(anonymous) @ messages.uvue:760
renderComponentRoot @ vue.runtime.esm.js:5065
componentUpdateFn @ vue.runtime.esm.js:5192
run @ vue.runtime.esm.js:180
instance.update @ vue.runtime.esm.js:5216
callWithErrorHandling @ vue.runtime.esm.js:1356
flushJobs @ vue.runtime.esm.js:1563
Promise.then (async)
queueFlush @ vue.runtime.esm.js:1472
queueJob @ vue.runtime.esm.js:1466
(anonymous) @ vue.runtime.esm.js:5210
resetScheduling @ vue.runtime.esm.js:263
trigger @ vue.runtime.esm.js:403
set @ vue.runtime.esm.js:524
(anonymous) @ messages.uvue:760
callWithErrorHandling @ vue.runtime.esm.js:1356
callWithAsyncErrorHandling @ vue.runtime.esm.js:1363
invoke @ vue.runtime.esm.js:6223
invoker @ vue.runtime.esm.js:6235
mp.esm.js:529 [Vue warn]: Property "manageMode" was accessed during render but is not defined on instance.
at <Messages >
(anonymous) @ mp.esm.js:529
warn$1 @ vue.runtime.esm.js:1251
get @ vue.runtime.esm.js:2629
(anonymous) @ messages.uvue:760
vFor @ vue.runtime.esm.js:6364
f @ vue.runtime.esm.js:6718
(anonymous) @ messages.uvue:760
renderComponentRoot @ vue.runtime.esm.js:5065
componentUpdateFn @ vue.runtime.esm.js:5192
run @ vue.runtime.esm.js:180
instance.update @ vue.runtime.esm.js:5216
callWithErrorHandling @ vue.runtime.esm.js:1356
flushJobs @ vue.runtime.esm.js:1563
Promise.then (async)
queueFlush @ vue.runtime.esm.js:1472
queueJob @ vue.runtime.esm.js:1466
(anonymous) @ vue.runtime.esm.js:5210
resetScheduling @ vue.runtime.esm.js:263
trigger @ vue.runtime.esm.js:403
set @ vue.runtime.esm.js:524
(anonymous) @ messages.uvue:760
callWithErrorHandling @ vue.runtime.esm.js:1356
callWithAsyncErrorHandling @ vue.runtime.esm.js:1363
invoke @ vue.runtime.esm.js:6223
invoker @ vue.runtime.esm.js:6235
mp.esm.js:529 [Vue warn]: Property "manageMode" was accessed during render but is not defined on instance.
at <Messages >
(anonymous) @ mp.esm.js:529
warn$1 @ vue.runtime.esm.js:1251
get @ vue.runtime.esm.js:2629
(anonymous) @ messages.uvue:760
vFor @ vue.runtime.esm.js:6364
f @ vue.runtime.esm.js:6718
(anonymous) @ messages.uvue:760
renderComponentRoot @ vue.runtime.esm.js:5065
componentUpdateFn @ vue.runtime.esm.js:5192
run @ vue.runtime.esm.js:180
instance.update @ vue.runtime.esm.js:5216
callWithErrorHandling @ vue.runtime.esm.js:1356
flushJobs @ vue.runtime.esm.js:1563
Promise.then (async)
queueFlush @ vue.runtime.esm.js:1472
queueJob @ vue.runtime.esm.js:1466
(anonymous) @ vue.runtime.esm.js:5210
resetScheduling @ vue.runtime.esm.js:263
trigger @ vue.runtime.esm.js:403
set @ vue.runtime.esm.js:524
(anonymous) @ messages.uvue:760
callWithErrorHandling @ vue.runtime.esm.js:1356
callWithAsyncErrorHandling @ vue.runtime.esm.js:1363
invoke @ vue.runtime.esm.js:6223
invoker @ vue.runtime.esm.js:6235