Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
669 lines
26 KiB
JavaScript
669 lines
26 KiB
JavaScript
/**
|
||
* 意见反馈:校验 → 可选上传截图(最多 3 张,单张 ≤5MB)→ POST /api/feedback/add
|
||
* 上游 type: 1-功能建议、2-播放问题、3-账号问题、4-其他
|
||
*/
|
||
(function () {
|
||
var MAX_FILES = 3;
|
||
var MAX_SIZE = 5 * 1024 * 1024;
|
||
var ACCEPT_TYPES = { "image/png": 1, "image/jpeg": 1, "image/jpg": 1 };
|
||
var CITY_OPTIONS = {
|
||
"北京市": "北京市",
|
||
"天津市": "天津市",
|
||
"河北省": "石家庄市|唐山市|秦皇岛市|邯郸市|邢台市|保定市|张家口市|承德市|沧州市|廊坊市|衡水市",
|
||
"山西省": "太原市|大同市|阳泉市|长治市|晋城市|朔州市|晋中市|运城市|忻州市|临汾市|吕梁市",
|
||
"内蒙古自治区": "呼和浩特市|包头市|乌海市|赤峰市|通辽市|鄂尔多斯市|呼伦贝尔市|巴彦淖尔市|乌兰察布市|兴安盟|锡林郭勒盟|阿拉善盟",
|
||
"辽宁省": "沈阳市|大连市|鞍山市|抚顺市|本溪市|丹东市|锦州市|营口市|阜新市|辽阳市|盘锦市|铁岭市|朝阳市|葫芦岛市",
|
||
"吉林省": "长春市|吉林市|四平市|辽源市|通化市|白山市|松原市|白城市|延边朝鲜族自治州",
|
||
"黑龙江省": "哈尔滨市|齐齐哈尔市|鸡西市|鹤岗市|双鸭山市|大庆市|伊春市|佳木斯市|七台河市|牡丹江市|黑河市|绥化市|大兴安岭地区",
|
||
"上海市": "上海市",
|
||
"江苏省": "南京市|无锡市|徐州市|常州市|苏州市|南通市|连云港市|淮安市|盐城市|扬州市|镇江市|泰州市|宿迁市",
|
||
"浙江省": "杭州市|宁波市|温州市|嘉兴市|湖州市|绍兴市|金华市|衢州市|舟山市|台州市|丽水市",
|
||
"安徽省": "合肥市|芜湖市|蚌埠市|淮南市|马鞍山市|淮北市|铜陵市|安庆市|黄山市|滁州市|阜阳市|宿州市|六安市|亳州市|池州市|宣城市",
|
||
"福建省": "福州市|厦门市|莆田市|三明市|泉州市|漳州市|南平市|龙岩市|宁德市",
|
||
"江西省": "南昌市|景德镇市|萍乡市|九江市|新余市|鹰潭市|赣州市|吉安市|宜春市|抚州市|上饶市",
|
||
"山东省": "济南市|青岛市|淄博市|枣庄市|东营市|烟台市|潍坊市|济宁市|泰安市|威海市|日照市|临沂市|德州市|聊城市|滨州市|菏泽市",
|
||
"河南省": "郑州市|开封市|洛阳市|平顶山市|安阳市|鹤壁市|新乡市|焦作市|濮阳市|许昌市|漯河市|三门峡市|南阳市|商丘市|信阳市|周口市|驻马店市|济源市",
|
||
"湖北省": "武汉市|黄石市|十堰市|宜昌市|襄阳市|鄂州市|荆门市|孝感市|荆州市|黄冈市|咸宁市|随州市|恩施土家族苗族自治州|仙桃市|潜江市|天门市|神农架林区",
|
||
"湖南省": "长沙市|株洲市|湘潭市|衡阳市|邵阳市|岳阳市|常德市|张家界市|益阳市|郴州市|永州市|怀化市|娄底市|湘西土家族苗族自治州",
|
||
"广东省": "广州市|韶关市|深圳市|珠海市|汕头市|佛山市|江门市|湛江市|茂名市|肇庆市|惠州市|梅州市|汕尾市|河源市|阳江市|清远市|东莞市|中山市|潮州市|揭阳市|云浮市",
|
||
"广西壮族自治区": "南宁市|柳州市|桂林市|梧州市|北海市|防城港市|钦州市|贵港市|玉林市|百色市|贺州市|河池市|来宾市|崇左市",
|
||
"海南省": "海口市|三亚市|三沙市|儋州市",
|
||
"重庆市": "重庆市",
|
||
"四川省": "成都市|自贡市|攀枝花市|泸州市|德阳市|绵阳市|广元市|遂宁市|内江市|乐山市|南充市|眉山市|宜宾市|广安市|达州市|雅安市|巴中市|资阳市|阿坝藏族羌族自治州|甘孜藏族自治州|凉山彝族自治州",
|
||
"贵州省": "贵阳市|六盘水市|遵义市|安顺市|毕节市|铜仁市|黔西南布依族苗族自治州|黔东南苗族侗族自治州|黔南布依族苗族自治州",
|
||
"云南省": "昆明市|曲靖市|玉溪市|保山市|昭通市|丽江市|普洱市|临沧市|楚雄彝族自治州|红河哈尼族彝族自治州|文山壮族苗族自治州|西双版纳傣族自治州|大理白族自治州|德宏傣族景颇族自治州|怒江傈僳族自治州|迪庆藏族自治州",
|
||
"西藏自治区": "拉萨市|日喀则市|昌都市|林芝市|山南市|那曲市|阿里地区",
|
||
"陕西省": "西安市|铜川市|宝鸡市|咸阳市|渭南市|延安市|汉中市|榆林市|安康市|商洛市",
|
||
"甘肃省": "兰州市|嘉峪关市|金昌市|白银市|天水市|武威市|张掖市|平凉市|酒泉市|庆阳市|定西市|陇南市|临夏回族自治州|甘南藏族自治州",
|
||
"青海省": "西宁市|海东市|海北藏族自治州|黄南藏族自治州|海南藏族自治州|果洛藏族自治州|玉树藏族自治州|海西蒙古族藏族自治州",
|
||
"宁夏回族自治区": "银川市|石嘴山市|吴忠市|固原市|中卫市",
|
||
"新疆维吾尔自治区": "乌鲁木齐市|克拉玛依市|吐鲁番市|哈密市|昌吉回族自治州|博尔塔拉蒙古自治州|巴音郭楞蒙古自治州|阿克苏地区|克孜勒苏柯尔克孜自治州|喀什地区|和田地区|伊犁哈萨克自治州|塔城地区|阿勒泰地区",
|
||
"香港特别行政区": "香港岛|九龙|新界",
|
||
"澳门特别行政区": "澳门半岛|氹仔|路环",
|
||
"台湾省": "台北市|新北市|桃园市|台中市|台南市|高雄市|基隆市|新竹市|嘉义市|新竹县|苗栗县|彰化县|南投县|云林县|嘉义县|屏东县|宜兰县|花莲县|台东县|澎湖县|金门县|连江县",
|
||
"海外": "海外",
|
||
};
|
||
|
||
var root = document.querySelector("[data-feedback-select]");
|
||
var form = document.querySelector("[data-feedback-form]");
|
||
var typeInput = document.querySelector("[data-feedback-type]");
|
||
var contentInput = document.querySelector("[data-feedback-content]");
|
||
var contactInput = document.querySelector("[data-feedback-contact]");
|
||
var fileInput = document.querySelector("[data-feedback-file]");
|
||
var fileText = document.querySelector("[data-feedback-file-text]");
|
||
var uploadEl = document.querySelector("[data-feedback-upload]");
|
||
var previewList = document.querySelector("[data-feedback-preview-list]");
|
||
var submitBtn = document.querySelector("[data-feedback-submit]");
|
||
var counter = document.querySelector("[data-feedback-counter]");
|
||
var typeChips = document.querySelectorAll("[data-feedback-type-option]");
|
||
var choiceGroups = document.querySelectorAll("[data-feedback-choice-group]");
|
||
var provinceSelect = document.querySelector("[data-feedback-province]");
|
||
var citySelect = document.querySelector("[data-feedback-city]");
|
||
var operatorSelect = document.querySelector("[data-feedback-operator]");
|
||
|
||
var trigger = root ? root.querySelector("[data-feedback-select-trigger]") : null;
|
||
var valueEl = root ? root.querySelector("[data-feedback-select-value]") : null;
|
||
var menu = root ? root.querySelector("[data-feedback-select-menu]") : null;
|
||
var options = root ? root.querySelectorAll(".feedback-select-option") : [];
|
||
var placeholder = valueEl ? valueEl.textContent : "";
|
||
var defaultSubmitText = submitBtn ? submitBtn.textContent : "提交反馈";
|
||
/** @type {{ id: string, file: File, url: string }[]} */
|
||
var selectedFiles = [];
|
||
var fileSeq = 0;
|
||
|
||
function setSelectPlaceholderState(select) {
|
||
if (!select) return;
|
||
select.classList.toggle("is-placeholder", !select.value);
|
||
}
|
||
|
||
function closeCustomSelect(wrap, restoreFocus) {
|
||
if (!wrap) return;
|
||
var custom = wrap._feedbackSelect;
|
||
if (!custom) return;
|
||
wrap.classList.remove("is-open");
|
||
custom.menu.hidden = true;
|
||
custom.trigger.setAttribute("aria-expanded", "false");
|
||
if (restoreFocus) custom.trigger.focus();
|
||
}
|
||
|
||
function closeAllCustomSelects(except) {
|
||
document.querySelectorAll(".feedback-select-wrap.is-open").forEach(function (wrap) {
|
||
if (wrap !== except) closeCustomSelect(wrap, false);
|
||
});
|
||
}
|
||
|
||
function focusCustomOption(menu, index) {
|
||
var items = menu.querySelectorAll(".feedback-select-menu-option");
|
||
if (!items.length) return;
|
||
var nextIndex = Math.max(0, Math.min(index, items.length - 1));
|
||
items[nextIndex].focus();
|
||
}
|
||
|
||
function openCustomSelect(wrap) {
|
||
if (!wrap) return;
|
||
var custom = wrap._feedbackSelect;
|
||
if (!custom || custom.select.disabled) return;
|
||
closeAllCustomSelects(wrap);
|
||
wrap.classList.add("is-open");
|
||
custom.menu.hidden = false;
|
||
custom.trigger.setAttribute("aria-expanded", "true");
|
||
}
|
||
|
||
function buildCustomSelectMenu(select) {
|
||
var wrap = select && select._feedbackSelectWrap;
|
||
var custom = wrap && wrap._feedbackSelect;
|
||
if (!custom) return;
|
||
custom.menu.innerHTML = "";
|
||
|
||
Array.prototype.forEach.call(select.options, function (nativeOption) {
|
||
if (nativeOption.disabled) return;
|
||
var option = document.createElement("button");
|
||
option.type = "button";
|
||
option.className = "feedback-select-menu-option";
|
||
option.textContent = nativeOption.textContent;
|
||
option.setAttribute("role", "option");
|
||
option.setAttribute("data-value", nativeOption.value);
|
||
option.setAttribute("aria-selected", nativeOption.selected ? "true" : "false");
|
||
option.classList.toggle("is-selected", nativeOption.selected);
|
||
|
||
option.addEventListener("click", function () {
|
||
select.value = nativeOption.value;
|
||
select.dispatchEvent(new Event("change", { bubbles: true }));
|
||
closeCustomSelect(wrap, true);
|
||
});
|
||
option.addEventListener("keydown", function (event) {
|
||
var items = Array.prototype.slice.call(
|
||
custom.menu.querySelectorAll(".feedback-select-menu-option"),
|
||
);
|
||
var index = items.indexOf(option);
|
||
if (event.key === "ArrowDown") {
|
||
event.preventDefault();
|
||
focusCustomOption(custom.menu, index + 1);
|
||
} else if (event.key === "ArrowUp") {
|
||
event.preventDefault();
|
||
focusCustomOption(custom.menu, index - 1);
|
||
} else if (event.key === "Home") {
|
||
event.preventDefault();
|
||
focusCustomOption(custom.menu, 0);
|
||
} else if (event.key === "End") {
|
||
event.preventDefault();
|
||
focusCustomOption(custom.menu, items.length - 1);
|
||
} else if (event.key === "Escape") {
|
||
event.preventDefault();
|
||
closeCustomSelect(wrap, true);
|
||
}
|
||
});
|
||
custom.menu.appendChild(option);
|
||
});
|
||
}
|
||
|
||
function syncCustomSelect(select) {
|
||
var wrap = select && select._feedbackSelectWrap;
|
||
var custom = wrap && wrap._feedbackSelect;
|
||
if (!custom) return;
|
||
var selected = select.options[select.selectedIndex];
|
||
custom.trigger.textContent = selected
|
||
? selected.textContent
|
||
: select.getAttribute("aria-label") || "请选择";
|
||
custom.trigger.disabled = select.disabled;
|
||
custom.trigger.classList.toggle("is-placeholder", !select.value);
|
||
wrap.classList.toggle("is-disabled", select.disabled);
|
||
if (select.disabled) closeCustomSelect(wrap, false);
|
||
buildCustomSelectMenu(select);
|
||
}
|
||
|
||
function initCustomSelect(select, index) {
|
||
if (!select || select._feedbackSelectWrap) return;
|
||
var wrap = document.createElement("div");
|
||
var triggerButton = document.createElement("button");
|
||
var customMenu = document.createElement("div");
|
||
var menuId = (select.id || "feedback-select-" + index) + "-menu";
|
||
|
||
wrap.className = "feedback-select-wrap";
|
||
triggerButton.type = "button";
|
||
triggerButton.className = "feedback-select-trigger";
|
||
triggerButton.setAttribute("aria-haspopup", "listbox");
|
||
triggerButton.setAttribute("aria-expanded", "false");
|
||
triggerButton.setAttribute("aria-controls", menuId);
|
||
triggerButton.setAttribute("aria-label", select.getAttribute("aria-label") || "请选择");
|
||
customMenu.className = "feedback-select-menu";
|
||
customMenu.id = menuId;
|
||
customMenu.setAttribute("role", "listbox");
|
||
customMenu.hidden = true;
|
||
|
||
select.parentNode.insertBefore(wrap, select);
|
||
wrap.appendChild(select);
|
||
wrap.appendChild(triggerButton);
|
||
wrap.appendChild(customMenu);
|
||
select.classList.add("feedback-select-native");
|
||
select.setAttribute("tabindex", "-1");
|
||
select.setAttribute("aria-hidden", "true");
|
||
select._feedbackSelectWrap = wrap;
|
||
wrap._feedbackSelect = { select: select, trigger: triggerButton, menu: customMenu };
|
||
|
||
triggerButton.addEventListener("click", function () {
|
||
if (wrap.classList.contains("is-open")) closeCustomSelect(wrap, false);
|
||
else openCustomSelect(wrap);
|
||
});
|
||
triggerButton.addEventListener("keydown", function (event) {
|
||
if (event.key !== "ArrowDown" && event.key !== "ArrowUp") return;
|
||
event.preventDefault();
|
||
openCustomSelect(wrap);
|
||
var selectedIndex = 0;
|
||
var items = customMenu.querySelectorAll(".feedback-select-menu-option");
|
||
items.forEach(function (item, itemIndex) {
|
||
if (item.classList.contains("is-selected")) selectedIndex = itemIndex;
|
||
});
|
||
focusCustomOption(customMenu, selectedIndex);
|
||
});
|
||
select.addEventListener("change", function () {
|
||
syncCustomSelect(select);
|
||
});
|
||
syncCustomSelect(select);
|
||
}
|
||
|
||
function initCustomSelects() {
|
||
[provinceSelect, citySelect, operatorSelect].forEach(initCustomSelect);
|
||
}
|
||
|
||
function renderCityOptions(province) {
|
||
if (!citySelect) return;
|
||
citySelect.innerHTML = "";
|
||
var placeholderOption = document.createElement("option");
|
||
placeholderOption.value = "";
|
||
placeholderOption.textContent = "城市(选填)";
|
||
placeholderOption.selected = true;
|
||
citySelect.appendChild(placeholderOption);
|
||
|
||
var citySource = CITY_OPTIONS[province] || "";
|
||
var cities = citySource ? citySource.split("|") : [];
|
||
cities.forEach(function (city) {
|
||
var option = document.createElement("option");
|
||
option.value = city;
|
||
option.textContent = city;
|
||
citySelect.appendChild(option);
|
||
});
|
||
citySelect.disabled = cities.length === 0;
|
||
setSelectPlaceholderState(citySelect);
|
||
syncCustomSelect(citySelect);
|
||
}
|
||
|
||
[provinceSelect, citySelect, operatorSelect].forEach(function (select) {
|
||
if (!select) return;
|
||
setSelectPlaceholderState(select);
|
||
select.addEventListener("change", function () {
|
||
setSelectPlaceholderState(select);
|
||
});
|
||
});
|
||
|
||
if (provinceSelect) {
|
||
provinceSelect.addEventListener("change", function () {
|
||
renderCityOptions(provinceSelect.value);
|
||
});
|
||
}
|
||
renderCityOptions(provinceSelect ? provinceSelect.value : "");
|
||
initCustomSelects();
|
||
|
||
function toast(message, type) {
|
||
if (window.AuthAPI && typeof AuthAPI.toast === "function") {
|
||
AuthAPI.toast(message, type);
|
||
return;
|
||
}
|
||
if (typeof showToast === "function") {
|
||
showToast(message, type);
|
||
return;
|
||
}
|
||
}
|
||
|
||
function setSubmitting(loading) {
|
||
if (!submitBtn) return;
|
||
submitBtn.disabled = !!loading;
|
||
submitBtn.classList.toggle("is-loading", !!loading);
|
||
submitBtn.textContent = loading ? "提交中..." : defaultSubmitText;
|
||
}
|
||
|
||
function updateUploadState() {
|
||
var count = selectedFiles.length;
|
||
if (uploadEl) uploadEl.classList.toggle("is-full", count >= MAX_FILES);
|
||
if (previewList) previewList.hidden = count === 0;
|
||
if (fileText) {
|
||
fileText.textContent =
|
||
count > 0
|
||
? "已选 " + count + "/" + MAX_FILES + " 个文件"
|
||
: "支持jpg/png 最多3个文件";
|
||
}
|
||
}
|
||
|
||
function renderPreviews() {
|
||
if (!previewList) return;
|
||
previewList.innerHTML = "";
|
||
selectedFiles.forEach(function (item) {
|
||
var li = document.createElement("li");
|
||
li.className = "feedback-upload-item";
|
||
li.setAttribute("data-file-id", item.id);
|
||
|
||
var img = document.createElement("img");
|
||
img.src = item.url;
|
||
img.alt = item.file.name || "截图预览";
|
||
|
||
var removeBtn = document.createElement("button");
|
||
removeBtn.type = "button";
|
||
removeBtn.className = "feedback-upload-remove";
|
||
removeBtn.setAttribute("aria-label", "移除图片");
|
||
removeBtn.textContent = "×";
|
||
removeBtn.addEventListener("click", function (event) {
|
||
event.preventDefault();
|
||
event.stopPropagation();
|
||
removeFile(item.id);
|
||
});
|
||
|
||
li.appendChild(img);
|
||
li.appendChild(removeBtn);
|
||
previewList.appendChild(li);
|
||
});
|
||
updateUploadState();
|
||
}
|
||
|
||
function clearFiles() {
|
||
selectedFiles.forEach(function (item) {
|
||
if (item.url) URL.revokeObjectURL(item.url);
|
||
});
|
||
selectedFiles = [];
|
||
if (fileInput) fileInput.value = "";
|
||
renderPreviews();
|
||
}
|
||
|
||
function removeFile(id) {
|
||
var next = [];
|
||
selectedFiles.forEach(function (item) {
|
||
if (item.id === id) {
|
||
if (item.url) URL.revokeObjectURL(item.url);
|
||
} else {
|
||
next.push(item);
|
||
}
|
||
});
|
||
selectedFiles = next;
|
||
if (fileInput) fileInput.value = "";
|
||
renderPreviews();
|
||
}
|
||
|
||
function isAcceptedImage(file) {
|
||
if (!file) return false;
|
||
if (file.type && ACCEPT_TYPES[file.type]) return true;
|
||
var name = String(file.name || "").toLowerCase();
|
||
return /\.(png|jpe?g)$/.test(name);
|
||
}
|
||
|
||
function addFiles(fileList) {
|
||
if (!fileList || !fileList.length) return;
|
||
var remain = MAX_FILES - selectedFiles.length;
|
||
if (remain <= 0) {
|
||
toast("最多上传 " + MAX_FILES + " 张图片", "error");
|
||
return;
|
||
}
|
||
|
||
var files = Array.prototype.slice.call(fileList, 0);
|
||
var added = 0;
|
||
var skippedSize = 0;
|
||
var skippedType = 0;
|
||
var skippedLimit = 0;
|
||
|
||
for (var i = 0; i < files.length; i++) {
|
||
if (added >= remain) {
|
||
skippedLimit += files.length - i;
|
||
break;
|
||
}
|
||
var file = files[i];
|
||
if (!isAcceptedImage(file)) {
|
||
skippedType += 1;
|
||
continue;
|
||
}
|
||
if (file.size > MAX_SIZE) {
|
||
skippedSize += 1;
|
||
continue;
|
||
}
|
||
fileSeq += 1;
|
||
selectedFiles.push({
|
||
id: "f" + fileSeq,
|
||
file: file,
|
||
url: URL.createObjectURL(file),
|
||
});
|
||
added += 1;
|
||
}
|
||
|
||
if (skippedType) toast("仅支持 PNG / JPG 图片", "error");
|
||
if (skippedSize) toast("单张图片不能超过 5MB", "error");
|
||
if (skippedLimit) toast("最多上传 " + MAX_FILES + " 张图片", "error");
|
||
|
||
renderPreviews();
|
||
}
|
||
|
||
function openMenu() {
|
||
if (!root) return;
|
||
root.classList.add("is-open");
|
||
if (menu) menu.hidden = false;
|
||
if (trigger) trigger.setAttribute("aria-expanded", "true");
|
||
}
|
||
|
||
function closeMenu() {
|
||
if (!root) return;
|
||
root.classList.remove("is-open");
|
||
if (menu) menu.hidden = true;
|
||
if (trigger) trigger.setAttribute("aria-expanded", "false");
|
||
}
|
||
|
||
function toggleMenu() {
|
||
if (!root) return;
|
||
if (root.classList.contains("is-open")) closeMenu();
|
||
else openMenu();
|
||
}
|
||
|
||
function selectOption(option) {
|
||
var value = option.getAttribute("data-value") || "";
|
||
var label = option.textContent.trim();
|
||
if (typeInput) typeInput.value = value;
|
||
if (valueEl) {
|
||
valueEl.textContent = label;
|
||
valueEl.classList.toggle("is-placeholder", !value);
|
||
}
|
||
options.forEach(function (item) {
|
||
var active = item === option;
|
||
item.classList.toggle("is-active", active);
|
||
item.setAttribute("aria-selected", active ? "true" : "false");
|
||
});
|
||
closeMenu();
|
||
}
|
||
|
||
if (trigger) {
|
||
trigger.addEventListener("click", function (event) {
|
||
event.preventDefault();
|
||
toggleMenu();
|
||
});
|
||
}
|
||
|
||
options.forEach(function (option) {
|
||
option.setAttribute("aria-selected", "false");
|
||
option.addEventListener("click", function () {
|
||
selectOption(option);
|
||
});
|
||
});
|
||
|
||
typeChips.forEach(function (chip) {
|
||
chip.addEventListener("click", function () {
|
||
typeChips.forEach(function (item) {
|
||
item.classList.toggle("is-active", item === chip);
|
||
});
|
||
if (typeInput) {
|
||
var nextType = chip.getAttribute("data-value") || "4";
|
||
typeInput.value = nextType;
|
||
typeInput.setAttribute("value", nextType);
|
||
}
|
||
});
|
||
});
|
||
|
||
choiceGroups.forEach(function (group) {
|
||
group.querySelectorAll(".feedback-chip").forEach(function (chip) {
|
||
chip.addEventListener("click", function () {
|
||
group.querySelectorAll(".feedback-chip").forEach(function (item) {
|
||
item.classList.toggle("is-active", item === chip);
|
||
});
|
||
});
|
||
});
|
||
});
|
||
|
||
function updateCounter() {
|
||
if (!counter || !contentInput) return;
|
||
counter.textContent = String(contentInput.value.length) + "/500";
|
||
}
|
||
|
||
if (contentInput) contentInput.addEventListener("input", updateCounter);
|
||
|
||
document.addEventListener("click", function (event) {
|
||
if (root && !root.contains(event.target)) closeMenu();
|
||
if (!event.target.closest(".feedback-select-wrap")) closeAllCustomSelects(null);
|
||
});
|
||
|
||
document.addEventListener("keydown", function (event) {
|
||
if (event.key === "Escape") closeMenu();
|
||
if (event.key === "Escape") closeAllCustomSelects(null);
|
||
});
|
||
|
||
if (fileInput) {
|
||
fileInput.addEventListener("change", function () {
|
||
addFiles(fileInput.files);
|
||
fileInput.value = "";
|
||
});
|
||
}
|
||
|
||
function requireLogin() {
|
||
if (window.CookieUtil && CookieUtil.isLoggedIn()) return true;
|
||
toast("请先登录后再提交反馈", "error");
|
||
var loginURL = "/login/return/feedback/";
|
||
if (typeof window.openLoginDialog === "function") {
|
||
var opened = window.openLoginDialog();
|
||
if (opened && typeof opened.then === "function") {
|
||
opened.catch(function () {
|
||
window.location.href = loginURL;
|
||
});
|
||
}
|
||
} else {
|
||
window.location.href = loginURL;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
function uploadAllImages() {
|
||
if (!selectedFiles.length) return Promise.resolve([]);
|
||
if (!window.AuthAPI || typeof AuthAPI.uploadImage !== "function") {
|
||
return Promise.reject(new Error("上传组件未加载"));
|
||
}
|
||
var urls = [];
|
||
var chain = Promise.resolve();
|
||
selectedFiles.forEach(function (item) {
|
||
chain = chain.then(function () {
|
||
return AuthAPI.uploadImage(item.file).then(function (data) {
|
||
var url = (data && (data.url || data.sourceNameFs)) || "";
|
||
if (!url) throw new Error("上传失败");
|
||
urls.push(url);
|
||
});
|
||
});
|
||
});
|
||
return chain.then(function () {
|
||
return urls;
|
||
});
|
||
}
|
||
|
||
if (!form) return;
|
||
|
||
if (typeInput && typeChips.length) {
|
||
var initialTypeChip = document.querySelector("[data-feedback-type-option].is-active") || typeChips[0];
|
||
typeInput.value = initialTypeChip.getAttribute("data-value") || "2";
|
||
}
|
||
updateUploadState();
|
||
|
||
form.addEventListener("submit", function (event) {
|
||
event.preventDefault();
|
||
|
||
if (!window.HttpRequest) {
|
||
return;
|
||
}
|
||
if (submitBtn && submitBtn.classList.contains("is-loading")) return;
|
||
if (!requireLogin()) return;
|
||
|
||
var activeTypeChip = document.querySelector("[data-feedback-type-option].is-active");
|
||
var type = parseInt(
|
||
activeTypeChip
|
||
? activeTypeChip.getAttribute("data-value")
|
||
: typeInput && typeInput.value
|
||
? typeInput.value
|
||
: "",
|
||
10,
|
||
);
|
||
if (!(type >= 1 && type <= 4)) {
|
||
toast("请选择问题类型", "error");
|
||
openMenu();
|
||
if (valueEl) {
|
||
valueEl.textContent = placeholder;
|
||
valueEl.classList.add("is-placeholder");
|
||
}
|
||
return;
|
||
}
|
||
|
||
var content = contentInput ? String(contentInput.value || "").trim() : "";
|
||
if (!content) {
|
||
toast("请填写问题描述", "error");
|
||
if (contentInput) contentInput.focus();
|
||
return;
|
||
}
|
||
if (content.length > 500) {
|
||
toast("问题描述最多 500 字", "error");
|
||
return;
|
||
}
|
||
if (selectedFiles.length > MAX_FILES) {
|
||
toast("最多上传 " + MAX_FILES + " 张图片", "error");
|
||
return;
|
||
}
|
||
for (var i = 0; i < selectedFiles.length; i++) {
|
||
if (selectedFiles[i].file.size > MAX_SIZE) {
|
||
toast("单张图片不能超过 5MB", "error");
|
||
return;
|
||
}
|
||
}
|
||
|
||
var contact = contactInput ? String(contactInput.value || "").trim() : "";
|
||
if (!contact) {
|
||
toast("请填写联系方式", "error");
|
||
if (contactInput) contactInput.focus();
|
||
return;
|
||
}
|
||
setSubmitting(true);
|
||
|
||
uploadAllImages()
|
||
.then(function (screenshots) {
|
||
var body = {
|
||
type: type,
|
||
content: content,
|
||
};
|
||
if (contact) body.contact = contact;
|
||
if (screenshots && screenshots.length) body.screenshots = screenshots;
|
||
return HttpRequest.post("/api/feedback/add", body);
|
||
})
|
||
.then(function (res) {
|
||
if (!res || res.code !== 200) {
|
||
var err = new Error((res && (res.tip || res.msg)) || "提交失败");
|
||
err.code = res && res.code;
|
||
err.data = res;
|
||
throw err;
|
||
}
|
||
toast((res && res.tip) || "提交成功", "success");
|
||
form.reset();
|
||
renderCityOptions("");
|
||
setSelectPlaceholderState(provinceSelect);
|
||
setSelectPlaceholderState(operatorSelect);
|
||
syncCustomSelect(provinceSelect);
|
||
syncCustomSelect(operatorSelect);
|
||
if (typeInput) typeInput.value = "";
|
||
if (valueEl) {
|
||
valueEl.textContent = placeholder;
|
||
valueEl.classList.add("is-placeholder");
|
||
}
|
||
options.forEach(function (item) {
|
||
item.classList.remove("is-active");
|
||
item.setAttribute("aria-selected", "false");
|
||
});
|
||
typeChips.forEach(function (item, index) {
|
||
item.classList.toggle("is-active", index === 0);
|
||
});
|
||
if (typeInput && typeChips.length) {
|
||
typeInput.value = typeChips[0].getAttribute("data-value") || "2";
|
||
}
|
||
choiceGroups.forEach(function (group) {
|
||
var groupChips = group.querySelectorAll(".feedback-chip");
|
||
groupChips.forEach(function (item, index) {
|
||
item.classList.toggle("is-active", index === 0 && group.hasAttribute("data-default-first"));
|
||
});
|
||
});
|
||
updateCounter();
|
||
clearFiles();
|
||
})
|
||
.catch(function (err) {
|
||
if (err && err.code === 401) {
|
||
toast("请先登录后再提交反馈", "error");
|
||
if (typeof window.openLoginDialog === "function") {
|
||
var opened = window.openLoginDialog();
|
||
if (opened && typeof opened.then === "function") {
|
||
opened.catch(function () {
|
||
window.location.href = "/login/return/feedback/";
|
||
});
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
toast((err && err.message) || "提交失败", "error");
|
||
})
|
||
.finally(function () {
|
||
setSubmitting(false);
|
||
});
|
||
});
|
||
})();
|