message-select.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. // module/chats/components/message-select/message-select.ts
  2. interface Option {
  3. id: string;
  4. name: string;
  5. checked?: boolean;
  6. options: Option[] | null;
  7. }
  8. interface HandleEvent {
  9. mark: {
  10. type: "sub" | "options";
  11. index: number;
  12. item: Option;
  13. };
  14. }
  15. Component({
  16. properties: {
  17. payload: {
  18. type: Object,
  19. value: { title: "", multiple: false, options: [] },
  20. result: "",
  21. belongNew: null,
  22. },
  23. active: { type: Boolean, value: false },
  24. },
  25. data: {
  26. options: [] as Option[],
  27. subOptions: [] as Option[],
  28. subMultiple: false,
  29. itemHeight: 48,
  30. result: "",
  31. hasSelected: false,
  32. leftTitle: "都没有",
  33. rightTitle: "提交",
  34. initialOptions: [] as Option[], // 保存初始状态
  35. hasChanged: false, // 记录是否发生了变化
  36. },
  37. lifetimes: {
  38. attached() {
  39. console.log("attached", this.data.payload.belongNew,this.data.payload);
  40. if (this.data.payload.belongNew) {
  41. this.setData({
  42. leftTitle: "都没有",
  43. rightTitle: "提交",
  44. });
  45. } else {
  46. this.setData({
  47. leftTitle: "无变化",
  48. rightTitle: "完成",
  49. });
  50. }
  51. },
  52. },
  53. observers: {
  54. "payload.options"(options: any) {
  55. this.setData({ options });
  56. // 保存初始状态(深拷贝)
  57. const initialOptions = JSON.parse(JSON.stringify(options));
  58. this.setData({ initialOptions });
  59. },
  60. options(options: AnyArray) {
  61. console.log("options", options);
  62. const hasSelected = options
  63. .filter((option: any) => !option.hide)
  64. .some((option: any) => option.checked);
  65. // 检测是否与初始状态不同
  66. const hasChanged = this._checkOptionsChanged(options, this.data.initialOptions);
  67. this.setData({
  68. hasSelected,
  69. hasChanged,
  70. });
  71. },
  72. },
  73. methods: {
  74. // 检测选项是否发生变化
  75. _checkOptionsChanged(currentOptions: Option[], initialOptions: Option[]): boolean {
  76. if (!initialOptions || initialOptions.length === 0) {
  77. return false;
  78. }
  79. // 比较主选项的选中状态
  80. for (let i = 0; i < currentOptions.length; i++) {
  81. const current = currentOptions[i];
  82. const initial = initialOptions[i];
  83. if (!initial) continue;
  84. // 比较主选项的checked状态
  85. if (current.checked !== initial.checked) {
  86. return true;
  87. }
  88. // 如果有子选项,比较子选项的选中状态
  89. if (current.options && initial.options) {
  90. for (let j = 0; j < current.options.length; j++) {
  91. const currentSub = current.options[j];
  92. const initialSub = initial.options[j];
  93. if (!initialSub) continue;
  94. if (currentSub.checked !== initialSub.checked) {
  95. return true;
  96. }
  97. }
  98. }
  99. }
  100. return false;
  101. },
  102. handleTop(event: HandleEvent) {
  103. const {
  104. mark: { item },
  105. } = event;
  106. console.log("handleTop", item, this.data.active);
  107. if (!item || !this.data.active) return;
  108. console.log("handleTop", this.data.subOptions);
  109. const index = this.data.options.findIndex(
  110. (option) => option.id === item.id
  111. );
  112. // 如果当前项已选中且已有已选中的子项,则直接弹出子项程度选择弹窗以便修改
  113. const itemInState: any = this.data.options[index];
  114. const hasSubOptions = itemInState?.options?.filter(
  115. (o: any) => !o?.hide
  116. ).length;
  117. const hasAnySubChecked = itemInState?.options?.some(
  118. (o: any) => o?.checked
  119. );
  120. console.log("itemInState", itemInState, hasSubOptions, hasAnySubChecked);
  121. if (itemInState?.checked && hasSubOptions && hasAnySubChecked) {
  122. this.setData({
  123. subTitle: itemInState.name,
  124. subOptions: itemInState.options,
  125. subMultiple: itemInState.css === "checkbox",
  126. });
  127. this.onCancel = () => {
  128. this.setData({ subOptions: [] });
  129. };
  130. this.onConfirm = () => {
  131. if (!this.data.subOptions.some((option) => option.checked)) {
  132. wx.showToast({ title: "请至少选择一项", icon: "error" });
  133. } else {
  134. const options = this.data.options;
  135. options[index].options = this.data.subOptions;
  136. this.setData({ subOptions: [], options });
  137. // 单选场景:选择/修改子程度后直接提交
  138. if (!this.data.payload.multiple) {
  139. this.onSubmit();
  140. }
  141. }
  142. };
  143. return;
  144. }
  145. const multiple = this.data.payload.multiple;
  146. const checked = !item.checked;
  147. const options = this._handle(this.data.options, item, index, multiple);
  148. this.setData({ options });
  149. // 注释掉自动提交的逻辑,让用户可以继续选择症状程度
  150. if (checked && !multiple) {
  151. this.onSubmit();
  152. }
  153. },
  154. handleSub(event: HandleEvent) {
  155. const {
  156. mark: { item },
  157. } = event;
  158. console.log("handleSub", item, this.data.active);
  159. if (!item || !this.data.active) return;
  160. console.log("this.data.subOptions", this.data.subOptions);
  161. const index = this.data.subOptions.findIndex(
  162. (option) => option.id === item.id
  163. );
  164. console.log("index", index);
  165. const multiple = this.data.subMultiple;
  166. const checked = !item.checked;
  167. const subOptions = this._handle(
  168. this.data.subOptions,
  169. item,
  170. index,
  171. multiple
  172. );
  173. this.setData({ subOptions });
  174. console.log(this.data.subOptions, "suboptions111");
  175. // 检查是否所有症状程度都被取消选中
  176. const hasAnySubChecked = subOptions.some((option) => option.checked);
  177. if (!hasAnySubChecked) {
  178. // 如果没有任何症状程度被选中,则取消整个症状的选中状态
  179. const mainIndex = this.data.options.findIndex(
  180. (option) => option.name === this.data.subTitle
  181. );
  182. if (mainIndex !== -1) {
  183. const options = this.data.options;
  184. options[mainIndex].checked = false;
  185. options[mainIndex].options = options[mainIndex].options?.map(
  186. (option: any) => {
  187. if (!option?.hide) option.checked = false;
  188. return option;
  189. }
  190. );
  191. // 关闭当前弹窗,避免遮挡导致切换其它症状时看起来未选中
  192. this.setData({ options, subOptions: [], subTitle: "" });
  193. return;
  194. }
  195. }
  196. // 注释掉自动关闭弹窗的逻辑,让用户可以继续修改症状程度
  197. if (checked && !multiple) {
  198. this.onConfirm();
  199. }
  200. },
  201. _handle(
  202. options: Option[],
  203. item: Option,
  204. index: number,
  205. multiple?: boolean
  206. ) {
  207. const checked = !item.checked;
  208. if (checked) {
  209. const fn = () => {
  210. if (multiple) {
  211. options[index].checked = checked;
  212. } else {
  213. options.forEach((option) => {
  214. option.checked = option.id === item.id;
  215. });
  216. }
  217. return options;
  218. };
  219. if (item.options?.filter((option) => !(<any>option)?.hide).length) {
  220. this.setData({
  221. subTitle: item.name,
  222. subOptions: item.options,
  223. subMultiple: (<any>item).css === "checkbox",
  224. });
  225. this.onCancel = () => {
  226. this.setData({ subOptions: [] });
  227. };
  228. this.onConfirm = () => {
  229. if (!this.data.subOptions.some((option) => option.checked)) {
  230. wx.showToast({ title: "请至少选择一项", icon: "error" });
  231. } else {
  232. const options = fn();
  233. options[index].options = this.data.subOptions;
  234. this.setData({ subOptions: [], options });
  235. // 单选场景:选择子程度“确定”后直接提交
  236. if (!this.data.payload.multiple) {
  237. this.onSubmit();
  238. }
  239. }
  240. };
  241. } else {
  242. return fn();
  243. }
  244. } else {
  245. options[index].checked = !item.checked;
  246. if (item.options)
  247. options[index].options = item.options.map((option) => {
  248. if (!(<any>option)?.hide) option.checked = !item.checked;
  249. return option;
  250. });
  251. }
  252. return options;
  253. },
  254. onCancel() {},
  255. onConfirm() {},
  256. onSubmit() {
  257. console.log("onSubmit", this.data.result, this.data.hasSelected);
  258. if (this.data.result) return;
  259. if (!this.data.hasSelected) {
  260. wx.showToast({ title: "请至少选择一项", icon: "error" });
  261. } else {
  262. console.log(this.data.options);
  263. const result = this.data.options
  264. .filter((item: any) => item?.checked && !item?.hide)
  265. .map((option: any) => {
  266. const sub = option.options
  267. ?.filter((item: any) => item?.checked && !item?.hide)
  268. .map((item: any) => item.name)
  269. .join(", ");
  270. return [option.name, sub].filter(Boolean).join(": ");
  271. })
  272. .join("; ");
  273. console.log("result", result);
  274. this.setData({ result });
  275. this.triggerEvent("next", { options: this.data.options });
  276. }
  277. },
  278. onSkip() {
  279. console.log("onSkip", this.data.result, this.data.hasSelected);
  280. if (this.data.result) return;
  281. // 如果是新用户(belongNew为true),使用原来的逻辑
  282. if (this.data.payload.belongNew) {
  283. if (this.data.hasSelected) return;
  284. if (!this.data.payload.required) {
  285. this.setData({ result: "都没有" });
  286. this.triggerEvent("next", { options: this.data.options });
  287. }
  288. } else {
  289. // 如果是老用户(belongNew为false),检查是否发生了变化
  290. if (this.data.hasChanged) {
  291. // 如果发生了变化,不允许点击"无变化"
  292. // wx.showToast({ title: "症状已发生变化,请重新选择", icon: "none" });
  293. return;
  294. }
  295. // 如果没有变化,允许点击"无变化"
  296. this.setData({ result: "无变化" });
  297. this.triggerEvent("next", { options: this.data.options });
  298. }
  299. },
  300. },
  301. });