message-select.ts 9.4 KB

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