Initial project commit

This commit is contained in:
2026-05-09 16:40:29 +08:00
commit 02b0259a9e
267 changed files with 54891 additions and 0 deletions
+11
View File
@@ -0,0 +1,11 @@
export const checkLogin = () => {
const token = uni.getStorageSync("access_token");
if (!token) {
uni.showToast({ title: "请先登录", icon: "none" });
setTimeout(() => {
uni.navigateTo({ url: "/pages/login/index" });
}, 300);
return false;
}
return true;
};
+38
View File
@@ -0,0 +1,38 @@
const cityData = [
{ province: "北京市", cities: ["北京市"] },
{ province: "天津市", cities: ["天津市"] },
{ province: "上海市", cities: ["上海市"] },
{ province: "重庆市", cities: ["重庆市"] },
{ province: "河北省", cities: ["石家庄市","唐山市","秦皇岛市","邯郸市","邢台市","保定市","张家口市","承德市","沧州市","廊坊市","衡水市"] },
{ province: "山西省", cities: ["太原市","大同市","阳泉市","长治市","晋城市","朔州市","晋中市","运城市","忻州市","临汾市","吕梁市"] },
{ province: "辽宁省", cities: ["沈阳市","大连市","鞍山市","抚顺市","本溪市","丹东市","锦州市","营口市","阜新市","辽阳市","盘锦市","铁岭市","朝阳市","葫芦岛市"] },
{ province: "吉林省", cities: ["长春市","吉林市","四平市","辽源市","通化市","白山市","松原市","白城市","延边朝鲜族自治州"] },
{ province: "黑龙江省", cities: ["哈尔滨市","齐齐哈尔市","鸡西市","鹤岗市","双鸭山市","大庆市","伊春市","佳木斯市","七台河市","牡丹江市","黑河市","绥化市","大兴安岭地区"] },
{ province: "江苏省", cities: ["南京市","无锡市","徐州市","常州市","苏州市","南通市","连云港市","淮安市","盐城市","扬州市","镇江市","泰州市","宿迁市"] },
{ province: "浙江省", cities: ["杭州市","宁波市","温州市","嘉兴市","湖州市","绍兴市","金华市","衢州市","舟山市","台州市","丽水市"] },
{ province: "安徽省", cities: ["合肥市","芜湖市","蚌埠市","淮南市","马鞍山市","淮北市","铜陵市","安庆市","黄山市","滁州市","阜阳市","宿州市","六安市","亳州市","池州市","宣城市"] },
{ province: "福建省", cities: ["福州市","厦门市","莆田市","三明市","泉州市","漳州市","南平市","龙岩市","宁德市"] },
{ province: "江西省", cities: ["南昌市","景德镇市","萍乡市","九江市","新余市","鹰潭市","赣州市","吉安市","宜春市","抚州市","上饶市"] },
{ province: "山东省", cities: ["济南市","青岛市","淄博市","枣庄市","东营市","烟台市","潍坊市","济宁市","泰安市","威海市","日照市","临沂市","德州市","聊城市","滨州市","菏泽市"] },
{ province: "河南省", cities: ["郑州市","开封市","洛阳市","平顶山市","安阳市","鹤壁市","新乡市","焦作市","濮阳市","许昌市","漯河市","三门峡市","南阳市","商丘市","信阳市","周口市","驻马店市"] },
{ province: "湖北省", cities: ["武汉市","黄石市","十堰市","宜昌市","襄阳市","鄂州市","荆门市","孝感市","荆州市","黄冈市","咸宁市","随州市","恩施土家族苗族自治州"] },
{ province: "湖南省", cities: ["长沙市","株洲市","湘潭市","衡阳市","邵阳市","岳阳市","常德市","张家界市","益阳市","郴州市","永州市","怀化市","娄底市","湘西土家族苗族自治州"] },
{ province: "广东省", cities: ["广州市","韶关市","深圳市","珠海市","汕头市","佛山市","江门市","湛江市","茂名市","肇庆市","惠州市","梅州市","汕尾市","河源市","阳江市","清远市","东莞市","中山市","潮州市","揭阳市","云浮市"] },
{ province: "海南省", cities: ["海口市","三亚市","三沙市","儋州市"] },
{ province: "四川省", cities: ["成都市","自贡市","攀枝花市","泸州市","德阳市","绵阳市","广元市","遂宁市","内江市","乐山市","南充市","眉山市","宜宾市","广安市","达州市","雅安市","巴中市","资阳市","阿坝藏族羌族自治州","甘孜藏族自治州","凉山彝族自治州"] },
{ province: "贵州省", cities: ["贵阳市","六盘水市","遵义市","安顺市","毕节市","铜仁市","黔西南布依族苗族自治州","黔东南苗族侗族自治州","黔南布依族苗族自治州"] },
{ province: "云南省", cities: ["昆明市","曲靖市","玉溪市","保山市","昭通市","丽江市","普洱市","临沧市","楚雄彝族自治州","红河哈尼族彝族自治州","文山壮族苗族自治州","西双版纳傣族自治州","大理白族自治州","德宏傣族景颇族自治州","怒江傈僳族自治州","迪庆藏族自治州"] },
{ province: "西藏自治区", cities: ["拉萨市","日喀则市","昌都市","林芝市","山南市","那曲市","阿里地区"] },
{ province: "陕西省", cities: ["西安市","铜川市","宝鸡市","咸阳市","渭南市","延安市","汉中市","榆林市","安康市","商洛市"] },
{ province: "甘肃省", cities: ["兰州市","嘉峪关市","金昌市","白银市","天水市","武威市","张掖市","平凉市","酒泉市","庆阳市","定西市","陇南市","临夏回族自治州","甘南藏族自治州"] },
{ province: "青海省", cities: ["西宁市","海东市","海北藏族自治州","黄南藏族自治州","海南藏族自治州","果洛藏族自治州","玉树藏族自治州","海西蒙古族藏族自治州"] },
{ province: "内蒙古自治区", cities: ["呼和浩特市","包头市","乌海市","赤峰市","通辽市","鄂尔多斯市","呼伦贝尔市","巴彦淖尔市","乌兰察布市","兴安盟","锡林郭勒盟","阿拉善盟"] },
{ province: "广西壮族自治区", cities: ["南宁市","柳州市","桂林市","梧州市","北海市","防城港市","钦州市","贵港市","玉林市","百色市","贺州市","河池市","来宾市","崇左市"] },
{ province: "宁夏回族自治区", cities: ["银川市","石嘴山市","吴忠市","固原市","中卫市"] },
{ province: "新疆维吾尔自治区", cities: ["乌鲁木齐市","克拉玛依市","吐鲁番市","哈密市","昌吉回族自治州","博尔塔拉蒙古自治州","巴音郭楞蒙古自治州","阿克苏地区","克孜勒苏柯尔克孜自治州","喀什地区","和田地区","伊犁哈萨克自治州","塔城地区","阿勒泰地区"] },
{ province: "香港特别行政区", cities: ["香港"] },
{ province: "澳门特别行政区", cities: ["澳门"] },
{ province: "台湾省", cities: ["台北市","新北市","桃园市","台中市","台南市","高雄市","基隆市","新竹市","嘉义市"] },
];
export default cityData;
+11
View File
@@ -0,0 +1,11 @@
// #ifdef H5
const API_BASE = "/api/v1";
const SERVER_ORIGIN = window.location.origin;
// #endif
// #ifndef H5
const API_BASE = "http://10.0.10.11:8000/api/v1";
const SERVER_ORIGIN = "http://10.0.10.11:8000";
// #endif
export { API_BASE, SERVER_ORIGIN };
+7
View File
@@ -0,0 +1,7 @@
import { SERVER_ORIGIN } from "./config";
export const resolveImageUrl = (url) => {
if (!url) return "";
if (url.startsWith("http://") || url.startsWith("https://")) return url;
return SERVER_ORIGIN + url;
};
+157
View File
@@ -0,0 +1,157 @@
import { API_BASE } from "./config";
let isRefreshing = false;
let pendingQueue = [];
const isHtmlLike = (value) =>
typeof value === "string" && value.trim().startsWith("<");
export const extractList = (payload) => {
if (Array.isArray(payload?.items)) return payload.items;
if (Array.isArray(payload?.data)) return payload.data;
if (Array.isArray(payload)) return payload;
return [];
};
const doRequest = (options) => {
return new Promise((resolve, reject) => {
const token = uni.getStorageSync("access_token");
uni.request({
url: API_BASE + options.url,
method: options.method || "GET",
data: options.data,
header: {
"Content-Type": "application/json",
...(token ? { Authorization: `Bearer ${token}` } : {}),
...options.header,
},
success: (res) => resolve(res),
fail: (err) => reject(err),
});
});
};
const tryRefreshToken = () => {
return new Promise((resolve, reject) => {
const rt = uni.getStorageSync("refresh_token");
if (!rt) return reject(new Error("no_refresh_token"));
uni.request({
url: API_BASE + "/auth/refresh",
method: "POST",
header: { "Content-Type": "application/json" },
data: { refresh_token: rt },
success: (res) => {
if (res.statusCode >= 200 && res.statusCode < 300) {
const { access_token, refresh_token } = res.data;
uni.setStorageSync("access_token", access_token);
if (refresh_token) uni.setStorageSync("refresh_token", refresh_token);
resolve();
} else {
reject(new Error("refresh_failed"));
}
},
fail: () => reject(new Error("refresh_network_error")),
});
});
};
const goLogin = () => {
uni.removeStorageSync("access_token");
uni.removeStorageSync("refresh_token");
const pages = getCurrentPages();
const current = pages[pages.length - 1];
const route = current ? `/${current.route}` : "";
if (route !== "/pages/login/index") {
uni.navigateTo({ url: "/pages/login/index" });
}
};
const handleError = (res) => {
const code = res.statusCode;
if (code === 403) return "没有权限执行此操作";
if (code === 404) return "请求的资源不存在";
if (code === 409) return "数据冲突,请刷新重试";
if (code === 422) {
const detail = res.data?.detail;
if (Array.isArray(detail) && detail.length > 0) {
return detail.map((d) => d.msg || d.message).join("");
}
}
if (code === 429) return "操作过于频繁,请稍后再试";
if (code >= 500) return "服务器繁忙,请稍后重试";
const raw = res.data?.message || res.data?.detail || "请求失败";
return typeof raw === "string" ? raw : JSON.stringify(raw);
};
const request = (options) => {
return new Promise((resolve, reject) => {
doRequest(options)
.then((res) => {
if (isHtmlLike(res.data)) {
const msg = "接口返回异常,请检查网络或服务状态";
uni.showToast({ title: msg, icon: "none" });
return reject(new Error(msg));
}
if (res.statusCode === 401) {
if (isRefreshing) {
pendingQueue.push({ options, resolve, reject });
return;
}
isRefreshing = true;
tryRefreshToken()
.then(() => {
isRefreshing = false;
doRequest(options).then((r2) => {
if (r2.statusCode >= 200 && r2.statusCode < 300) {
resolve(r2.data);
} else {
const msg = handleError(r2);
uni.showToast({ title: msg, icon: "none" });
reject(new Error(msg));
}
});
pendingQueue.forEach((p) => {
doRequest(p.options).then((r2) => {
if (r2.statusCode >= 200 && r2.statusCode < 300) {
p.resolve(r2.data);
} else {
p.reject(new Error(handleError(r2)));
}
});
});
pendingQueue = [];
})
.catch(() => {
isRefreshing = false;
pendingQueue.forEach((p) => p.reject(new Error("登录已过期")));
pendingQueue = [];
goLogin();
reject(new Error("登录已过期,请重新登录"));
});
return;
}
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve(res.data);
} else {
const msg = handleError(res);
uni.showToast({ title: msg, icon: "none" });
reject(new Error(msg));
}
})
.catch((err) => {
uni.showToast({ title: "网络连接失败,请检查网络", icon: "none" });
reject(err);
});
});
};
export const get = (url, data) => request({ url, method: "GET", data });
export const post = (url, data) => request({ url, method: "POST", data });
export const put = (url, data) => request({ url, method: "PUT", data });
export const del = (url, data) => request({ url, method: "DELETE", data });
export default request;
+25
View File
@@ -0,0 +1,25 @@
export const formatSpotPrice = (spot) => {
if (!spot) {
return { label: "", isFree: false };
}
if (spot.is_free) {
return { label: "免费", isFree: true };
}
const min = spot.price_min;
const max = spot.price_max;
if (min != null && max != null) {
if (Number(min) === Number(max)) {
return { label: `收费 ¥${min}`, isFree: false };
}
return { label: `收费 ¥${min} - ¥${max}`, isFree: false };
}
if (min != null || max != null) {
return { label: `收费 ¥${min ?? max}`, isFree: false };
}
return { label: "收费", isFree: false };
};