统计页面 修改店铺选择,增加店铺搜索Combobox
This commit is contained in:
1 parent
b6b72e6bc3
commit
f12a7f14ea
16 files changed
+1801
-41
No files matched your search
@@ -0,0 +1,15 @@
|
||||
namespace TinyPinyin.Data
|
||||
{
|
||||
public static class PinyinCode1
|
||||
{
|
||||
public static byte[] PINYIN_CODE_PADDING = new byte[]
|
||||
{
|
||||
177,79,200,37,169,41,96,13,134,58,2,134,67,33,184,128,16,79,211,92,75,40,54,206,19,34,241,102,32,39,53,25,201,37,194,233,28,47,168,151,133,170,159,151,26,160,0,204,151,176,3,116,69,64,36,15,103,74,178,194,31,81,68,50,154,196,73,21,145,176,40,124,41,118,173,84,223,152,167,37,218,134,12,253,48,228,242,232,2,220,89,192,136,66,156,160,153,96,12,40,128,151,49,26,223,65,240,131,96,69,20,170,80,34,146,163,28,34,64,80,202,4,65,65,0,0,96,129,24,65,74,66,9,4,192,1,136,5,16,20,71,34,9,17,48,128,40,52,194,242,128,131,128,41,192,62,148,35,141,141,1,9,16,48,24,16,144,45,231,185,29,8,169,105,87,155,161,252,152,154,78,100,248,142,19,89,252,210,2,21,93,3,226,16,23,198,12,80,30,73,1,130,10,209,98,47,153,142,12,136,140,112,112,89,135,173,172,38,10,128,2,201,12,65,67,153,115,4,12,203,129,118,114,248,32,67,169,57,26,21,118,144,159,16,143,38,93,12,166,43,104,84,78,70,32,232,192,8,97,1,22,153,114,24,135,204,72,230,10,92,139,200,168,129,16,69,40,64,210,12,5,57,131,142,77,34,28,209,82,231,236,153,159,155,24,0,59,32,9,72,233,81,34,66,2,24,144,132,169,33,192,176,192,160,2,147,74,74,210,41,4,34,129,130,136,152,65,10,192,88,183,197,86,122,0,112,160,18,228,208,131,56,161,128,240,80,72,56,210,216,51,170,39,204,233,50,16,103,186,32,16,132,168,69,76,40,0,66,86,228,97,177,178,134,243,32,10,126,188,46,1,37,196,205,107,76,10,72,147,253,87,6,0,26,71,65,9,50,210,199,207,2,195,250,217,96,251,0,93,53,160,90,40,16,160,13,76,7,167,129,32,210,17,163,225,99,225,114,67,8,77,69,236,125,133,218,186,167,179,130,42,5,26,35,84,15,22,140,20,159,130,142,157,46,1,67,136,107,106,208,137,176,14,218,82,131,64,225,144,75,25,2,29,8,174,162,42,76,152,226,48,2,100,128,220,164,187,126,9,230,188,77,19,27,130,1,157,102,82,129,212,93,165,121,196,180,192,69,66,136,237,37,37,153,67,150,177,61,184,10,91,0,13,137,0,0,1,53,86,145,18,33,191,99,64,127,5,73,34,114,2,193,251,0,132,219,38,70,69,216,39,32,128,64,64,121,254,64,51,7,203,162,5,162,6,236,49,8,93,97,135,53,245,87,140,14,14,225,144,96,142,81,147,139,28,22,120,195,88,208,2,163,225,22,37,205,97,88,0,136,34,24,121,209,44,0,28,199,152,139,18,36,7,221,96,155,237,106,2,192,50,168,115,66,54,65,192,139,4,68,92,41,57,83,160,6,32,91,0,195,136,246,2,1,65,168,39,4,241,220,144,147,176,24,35,77,66,249,46,193,128,242,17,81,24,0,104,10,32,162,120,98,136,8,75,244,72,194,6,110,36,67,14,182,96,1,25,225,41,145,200,82,68,43,130,72,50,122,249,180,35,16,94,197,136,115,82,181,131,133,46,85,181,46,16,1,41,176,244,132,16,236,36,82,0,129,139,72,226,80,36,72,22,15,132,202,186,32,100,49,52,129,72,129,16,33,20,1,42,98,81,29,227,73,80,209,70,222,47,181,8,136,75,209,33,63,84,167,137,32,250,182,11,233,131,80,165,180,62,236,29,62,31,23,215,65,7,13,34,24,68,194,177,219,131,27,200,112,47,137,21,7,57,24,15,228,6,98,173,182,92,162,66,25,24,85,13,34,117,233,12,98,46,120,6,36,216,111,26,0,226,146,49,3,47,22,171,150,144,36,168,216,71,151,7,190,96,33,28,58,56,164,164,176,77,48,2,0,160,24,149,254,136,67,33,228,24,66,32,104,77,95,77,23,52
|
||||
};
|
||||
|
||||
public static byte[] PINYIN_CODE = new byte[]
|
||||
{
|
||||
104,68,147,250,33,86,0,78,127,24,33,86,129,21,111,197,92,40,40,139,255,241,39,39,3,19,103,49,70,49,37,69,3,172,69,110,172,100,19,25,41,138,96,99,253,134,129,134,87,105,43,32,175,143,136,0,78,58,80,136,136,169,139,244,90,104,104,207,0,138,138,75,192,104,0,133,84,124,121,82,166,134,246,236,254,121,104,37,37,104,106,0,199,138,250,103,85,88,92,69,0,0,41,0,39,129,209,130,0,39,0,0,188,181,0,17,85,100,90,30,206,93,0,0,0,0,252,133,107,93,181,175,104,141,166,0,111,132,39,39,80,41,111,158,111,114,121,250,84,136,49,54,98,98,99,90,99,250,99,129,70,79,146,57,133,111,104,32,118,205,0,88,136,68,172,118,136,103,0,20,110,90,58,171,76,80,12,12,129,0,79,104,37,12,166,68,120,135,249,40,6,127,135,134,19,13,49,88,24,182,0,25,144,39,57,127,90,87,87,30,119,69,12,252,93,96,63,57,176,104,34,33,22,33,104,205,194,12,130,34,101,252,134,241,78,84,131,130,102,87,25,12,79,86,63,84,134,250,238,111,66,75,81,104,91,146,104,129,2,84,129,90,82,94,135,11,36,90,60,134,110,128,126,111,53,114,24,80,43,35,99,87,33,33,182,25,98,91,80,136,38,96,210,20,102,216,216,90,8,95,176,135,37,4,49,13,49,130,241,104,49,2,132,65,112,188,58,136,21,4,14,34,48,80,63,136,151,110,101,64,126,115,14,75,35,111,104,88,151,101,222,69,216,96,139,109,76,252,0,143,0,238,123,115,165,88,96,101,7,82,201,130,207,19,129,118,128,107,5,66,133,48,104,39,92,37,75,145,133,92,162,104,38,154,106,169,106,39,196,136,95,110,3,180,204,80,182,70,30,38,98,100,135,104,17,131,86,130,116,184,0,133,131,27,254,155,31,222,224,135,84,120,137,37,131,55,40,0,184,139,41,68,37,75,20,208,114,15,73,111,85,51,77,3,95,157,153,84,142,104,90,164,147,254,169,109,127,136,87,24,234,52,90,85,169,197,246,10,111,49,86,91,94,111,64,35,40,0,100,172,169,162,49,131,94,90,115,139,89,234,131,16,64,85,87,99,3,11,111,91,14,38,33,133,19,117,102,53,170,78,162,25,145,96,105,11,65,41,41,194,60,59,141,45,92,240,60,120,104,250,64,93,136,134,95,33,134,84,133,151,140,145,139,252,216,182,143,83,183,50,168,127,70,144,12,84,139,207,23,131,125,103,133,30,1,0,107,37,252,100,227,134,46,130,134,80,111,19,21,64,80,242,100,87,60,83,77,90,35,38,145,63,151,30,68,11,103,125,102,126,3,100,110,131,95,124,31,90,14,133,145,197,129,104,90,98,49,73,27,131,232,70,70,11,115,179,134,80,86,33,158,51,38,52,86,90,112,15,169,17,114,101,187,9,65,60,114,134,85,32,252,141,25,41,24,11,89,109,102,57,56,79,82,19,130,57,116,60,0,18,41,228,117,167,53,109,119,145,240,50,5,43,111,125,147,33,253,253,38,30,112,127,1,100,63,85,179,11,243,135,171,180,189,252,87,3,107,70,139,88,32,254,137,73,150,249,85,165,33,106,173,250,62,32,80,127,83,126,43,67,58,133,138,29,86,87,141,77,133,131,69,175,20,102,87,52,87,132,200,103,135,130,254,241,87,135,2,23,104,142,224,32,104,59,136,96,155,131,41,58,133,30,116,0,18,3,17,58,40,31,163,216,135,252,195,84,214,2,216,33,174,167,184,157,10,72,16,117,133,49,110,114,0,36,169,63,80,178,41,32,9,41,126,169,183,117,228,60,100,167,209,80,84,114,117,112,93,39,128,93,87,106,74,148,74,197,71,33,80,74,80,135,71,49,100,100,39,39,59,252,71,86,191,91,71,7,136,169,157,17,79,212,5,172,111,6,100,177,85,0,163,100,65,105,92,19,250,139,65,144,0,101,131,40,129,104,129,32,137,191,8,212,112,191,94,8,27,137,27,116,103,137,191,135,191,101,95,197,196,15,106,90,145,142,224,104,196,39,105,195,134,149,112,201,152,0,90,90,196,19,70,58,94,87,19,121,39,141,121,157,103,168,235,90,200,70,87,174,86,131,136,41,193,33,250,102,142,50,136,172,1,66,176,70,93,131,106,50,2,169,25,201,142,53,49,76,135,175,175,222,85,72,129,83,83,83,87,139,41,0,87,0,0,90,87,246,87,144,125,144,93,62,246,4,93,155,71,5,41,129,59,112,112,119,60,66,60,12,12,44,86,255,104,129,145,252,54,41,81,129,58,92,122,78,141,169,113,174,177,120,94,44,90,41,4,139,32,200,176,134,235,17,134,134,10,169,32,17,32,136,103,98,60,44,158,153,76,80,133,42,5,30,48,148,134,107,48,107,144,76,129,64,136,179,183,120,112,55,97,148,161,252,30,43,103,131,55,169,64,85,248,32,250,44,144,94,78,20,129,76,1,100,143,131,129,20,100,139,128,38,131,76,73,84,103,90,38,131,96,124,144,44,140,32,72,180,169,89,32,243,152,133,103,254,141,122,124,143,171,139,241,177,107,133,107,131,131,60,128,129,131,104,131,133,32,52,203,169,136,169,99,5,8,100,130,84,188,174,136,150,90,133,70,136,226,134,4,34,104,136,203,169,136,165,165,140,152,101,76,89,204,157,134,174,115,39,148,136,114,20,200,38,164,109,109,197,148,98,140,1,180,21,195,162,166,144,197,70,95,95,145,84,104,98,82,38,165,205,180,243,39,129,0,253,133,5,88,104,254,83,140,69,139,58,90,188,98,98,184,169,35,9,5,10,34,114,138,10,101,84,114,0,0,92,92,10,49,0,93,240,139,61,96,249,3,237,90,100,56,138,2,14,122,11,210,38,84,138,104,115,132,146,132,157,121,86,4,83,107,255,25,157,85,121,111,107,158,126,58,158,171,171,53,72,138,4,85,241,4,104,3,100,15,216,4,39,91,252,217,22,147,38,84,126,8,39,85,78,122,90,78,11,147,143,90,58,188,208,58,129,20,43,21,157,15,21,126,143,180,110,180,85,103,83,90,134,134,80,4,2,133,191,106,80,34,129,6,181,39,140,90,95,135,6,84,129,77,1,85,0,33,112,77,68,169,129,3,169,99,99,100,35,133,124,236,0,148,99,133,27,236,64,169,35,120,68,149,55,85,112,27,112,88,100,169,141,30,65,41,138,0,Line truncated
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace TinyPinyin.Data
|
||||
{
|
||||
public static class PinyinCode2
|
||||
{
|
||||
public static byte[] PINYIN_CODE_PADDING = new byte[]
|
||||
{
|
||||
128,246,161,5,7,104,160,33,238,10,249,237,47,84,4,100,17,69,72,130,1,130,75,32,241,216,3,90,242,67,177,93,36,246,6,0,112,249,113,65,8,163,225,0,210,4,48,4,72,134,138,68,234,193,77,2,33,56,161,199,34,232,109,212,162,76,68,79,40,185,135,215,208,52,4,200,103,40,14,10,0,182,145,32,207,24,167,0,18,181,26,127,37,23,40,43,62,50,44,195,130,191,9,170,102,4,162,67,222,113,67,130,48,144,27,75,168,165,41,253,130,148,91,236,144,5,7,144,195,161,128,56,40,7,251,166,13,71,243,86,194,112,48,2,201,20,168,34,107,110,46,248,229,105,47,197,41,2,170,2,95,243,202,100,228,66,128,215,78,71,206,127,123,8,108,120,200,133,142,12,36,147,0,17,186,34,22,46,98,218,180,36,166,177,171,60,153,3,48,65,67,114,51,4,204,130,178,97,6,126,128,17,0,171,10,132,154,79,128,144,18,23,99,22,129,232,6,198,118,69,65,0,27,23,44,182,13,234,252,127,188,132,169,224,136,180,91,17,128,34,244,87,21,80,12,63,210,207,93,48,110,62,199,28,130,185,45,162,62,186,164,102,134,183,99,15,139,136,40,184,70,40,166,133,60,12,196,65,2,9,39,197,71,65,3,16,102,129,50,65,34,227,208,96,33,200,103,79,206,141,16,148,71,168,247,197,140,53,3,1,224,46,248,0,45,90,30,128,38,162,66,17,134,248,100,128,189,203,17,202,97,158,12,75,226,4,16,17,19,113,93,182,217,124,42,98,146,240,216,114,12,0,70,46,174,193,46,212,4,82,64,32,0,152,66,230,156,243,19,27,112,136,200,66,8,16,48,5,34,8,192,3,120,138,41,65,26,152,181,84,156,201,90,65,128,221,226,197,210,64,88,246,193,128,68,60,2,44,240,3,0,6,0,64,130,12,1,106,44,54,149,9,76,4,3,210,13,14,208,39,231,116,99,18,94,27,112,172,149,6,96,6,152,10,225,120,4,80,154,30,73,49,64,64,197,188,5,137,38,4,8,122,132,219,8,128,17,12,196,100,98,28,212,4,102,12,192,148,96,22,87,8,142,24,115,22,5,16,24,77,16,140,9,69,48,33,162,32,239,245,202,199,237,240,154,10,34,168,94,191,156,212,42,120,183,159,57,201,192,192,111,230,26,49,244,56,6,206,192,105,7,134,210,139,153,42,3,185,91,51,141,54,34,14,223,27,3,72,35,96,6,103,22,3,38,162,126,66,23,48,72,64,8,186,112,134,40,3,238,47,144,8,148,135,4,68,25,183,83,68,34,208,24,129,10,64,161,11,82,18,9,0,109,128,4,97,7,55,113,224,168,199,53,97,145,0,219,182,144,32,32,46,147,208,172,24,48,73,168,92,223,200,50,37,74,109,107,30,166,2,152,148,119,150,144,189,215,96,20,152,186,21,8,236,209,212,140,216,168,161,27,30,13,249,183,254,192,75,176,181,236,124,81,51,26,64,15,6,91,232,5,90,57,50,219,242,99,36,132,122,53,90,122,139,20,5,25,4,219,160,20,188,93,26,16,16,119,12,200,16,164,35,89,159,55,87,160,2,21,127,152,172,13,40,80,33,96,26,49,32,8,81,7,145,83,65,192,48,16,156,176,255,39,240,147,12,24,29,227,31,204,146,51,121,32,161,73,166,200,160,19,51,14,2,34,64,104,32,234,160,221,21,228,248,66,0,104,154,192,202,13,73,12,89,210,164,4,24,111,219,121,31,244,76,128,152,64,178,130,129,65,16,32,204,20,49,120,233,188,133,202,144,163,172,200,55,5,166,120,6,144,2,5,56,134,11,0,39,179,137,160,130,9,168,204,98,116,138,228,32,29,217,16,120,7,73,8,2,133,104,48,160,115,16,64,93,20,240,123,182,65,156,11,224,7,81,145,92,115,75,32,119,56,153,82
|
||||
};
|
||||
|
||||
public static byte[] PINYIN_CODE = new byte[]
|
||||
{
|
||||
205,191,41,121,121,171,168,68,208,111,110,193,50,96,96,107,131,242,67,129,134,103,41,47,111,50,80,193,63,129,134,144,3,107,16,118,179,166,0,107,245,0,92,59,163,114,111,36,184,139,0,0,0,90,130,104,126,90,228,196,164,15,102,131,139,57,102,131,9,30,112,144,201,52,130,102,134,125,93,85,124,252,187,55,112,90,15,39,133,53,114,68,177,15,60,6,125,49,38,57,148,85,102,250,147,95,55,235,61,96,98,65,225,129,48,101,45,253,30,252,123,193,95,94,95,143,75,254,101,65,130,131,149,0,178,18,136,0,85,250,171,126,109,252,109,92,92,72,122,250,28,53,13,110,121,132,121,124,158,104,104,95,146,107,107,26,189,135,63,140,166,164,36,49,169,94,1,45,83,69,105,129,56,167,180,172,196,179,34,52,148,41,60,16,180,138,41,124,41,127,194,203,219,101,66,240,136,30,85,5,118,131,49,0,0,253,0,107,80,91,133,254,149,49,249,41,122,158,131,150,113,126,85,98,65,82,93,203,84,254,10,175,177,254,87,20,83,126,75,165,114,47,73,37,60,195,139,37,52,141,141,59,126,129,228,88,75,222,19,136,69,123,86,2,8,118,28,102,177,165,95,41,0,0,0,0,129,71,0,180,0,0,112,57,41,132,59,175,224,106,85,54,32,149,96,37,93,139,149,104,0,249,100,167,87,126,59,129,54,20,14,68,41,137,107,129,130,130,1,129,131,253,60,104,16,50,35,175,169,30,195,106,61,58,197,250,0,18,128,129,252,196,222,104,95,131,106,80,1,100,250,196,128,107,46,129,158,247,62,41,0,197,110,133,106,252,167,167,22,180,0,52,184,199,126,232,184,133,95,72,112,169,85,136,51,171,0,41,0,136,180,100,169,136,36,134,77,52,123,220,111,178,162,0,87,0,139,89,176,107,131,106,110,107,88,224,20,32,163,139,45,35,80,49,5,4,0,0,111,183,169,117,181,59,141,0,163,163,136,167,169,6,209,111,176,0,252,48,124,91,111,111,252,232,95,34,41,250,144,104,141,85,95,86,111,155,164,156,48,85,81,104,250,121,38,0,156,145,156,145,43,30,0,106,91,90,111,252,89,104,96,84,59,135,232,121,64,124,95,239,85,89,121,35,0,171,41,104,145,111,47,124,133,132,48,21,84,250,21,21,77,139,252,38,27,38,27,134,54,54,169,55,111,169,107,57,57,49,131,129,203,203,102,203,51,101,65,38,57,33,95,98,41,24,141,243,251,3,52,1,114,171,104,89,133,103,24,127,58,129,103,0,114,81,40,18,64,135,33,106,66,51,126,52,104,58,72,132,171,18,72,131,131,41,232,73,136,106,1,104,30,148,148,102,98,65,126,126,102,6,129,104,232,126,73,104,89,84,105,205,193,193,2,151,72,111,14,14,14,241,241,14,32,191,0,0,241,0,130,126,23,205,75,98,80,15,87,139,205,114,3,71,0,59,238,139,76,53,14,24,0,191,54,41,111,75,115,131,57,24,184,205,169,69,15,33,249,180,126,26,126,195,180,4,67,39,63,200,141,190,250,244,207,250,60,87,43,86,14,212,0,90,37,70,1,250,106,85,111,101,3,99,148,1,99,70,58,184,1,101,114,114,46,46,132,19,109,59,46,166,216,74,83,107,68,133,3,18,120,197,52,126,66,112,30,143,43,78,83,57,85,75,190,3,250,32,234,112,252,84,84,98,49,17,100,132,38,84,0,0,60,133,38,252,196,102,79,1,136,19,142,119,58,5,129,15,15,93,81,134,84,93,141,121,0,250,86,95,95,0,104,83,114,112,113,100,37,36,58,110,75,121,128,250,205,16,193,57,197,84,39,65,14,30,133,238,235,141,115,101,177,193,120,87,232,169,182,25,87,80,121,203,193,41,139,117,75,75,76,115,169,196,104,90,85,110,65,133,128,102,126,100,49,157,137,139,90,3,104,130,134,5,20,126,196,12,143,41,166,110,102,119,93,82,191,49,121,246,48,83,133,52,222,37,176,237,20,250,49,216,139,113,136,38,167,96,97,90,235,200,58,101,129,109,105,13,97,178,180,58,183,90,247,120,136,106,135,134,104,126,126,149,37,106,80,120,131,101,174,49,129,80,92,90,22,144,133,106,84,85,147,136,132,183,0,3,70,104,204,167,104,196,5,135,247,80,89,90,119,95,52,157,61,255,139,80,135,17,246,98,93,133,106,124,201,128,76,251,234,84,4,177,104,130,136,252,132,133,131,39,143,27,0,126,129,177,32,127,121,224,98,135,174,3,80,129,142,112,9,190,143,110,85,20,71,124,119,104,249,107,163,114,164,112,169,99,90,84,169,46,87,104,111,69,165,111,135,130,39,82,193,54,37,238,87,37,71,159,245,220,112,136,89,35,217,71,109,89,87,68,77,52,74,140,28,64,169,46,49,167,46,61,72,165,162,171,80,83,114,124,63,0,20,131,127,27,90,105,92,40,44,99,47,127,103,151,78,112,75,70,115,83,139,93,172,127,57,143,65,255,61,140,144,85,89,250,102,109,112,175,60,135,240,114,33,41,250,84,38,180,210,139,61,49,167,133,240,85,50,65,241,58,111,216,111,180,93,196,136,176,182,106,53,4,123,111,217,37,243,46,121,112,162,127,1,100,252,65,198,133,106,196,12,112,81,11,85,1,112,148,129,35,112,27,180,144,72,0,131,197,241,85,111,112,37,37,16,124,136,131,227,111,3,68,4,72,87,124,20,83,83,63,80,81,17,90,27,80,96,94,100,119,96,196,148,191,107,100,110,119,198,92,193,116,127,207,158,39,77,234,193,171,250,250,193,65,50,80,24,72,197,95,203,95,129,239,131,131,121,87,88,104,106,126,39,134,131,125,59,111,14,200,39,71,38,109,139,134,0,3,133,0,106,60,178,128,112,208,8,110,5,45,172,32,100,46,220,144,78,39,189,107,161,158,0,131,95,179,107,92,0,0,247,135,107,60,112,56,112,171,102,195,142,38,148,58,57,76,177,101,26,201,124,39,104,182,187,249,80,169,23,84,85,81,253,27,39,52,104,131,51,114,94,106,15,127,52,52,216,57,39,17,80,235,41,41,236,82,25,199,115,65,114,125,85,144,63,133,107,90,134,122,96,144,61,63,54,14,133,126,108,106,95,178,133,100,35,189,107,46,184,163,181,89,18,59,111,94,121,14,16,133,132,15Line truncated
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace TinyPinyin.Data
|
||||
{
|
||||
public static class PinyinCode3
|
||||
{
|
||||
public static byte[] PINYIN_CODE_PADDING = new byte[]
|
||||
{
|
||||
176,9,83,102,151,4,40,15,197,134,34,208,132,7,1,50,66,128,4,45,104,73,121,252,179,147,135,146,136,110,52,222,137,42,137,128,202,46,52,188,148,25,143,53,9,143,186,1,104,1,128,3,140,76,112,150,114,97,11,206,76,152,80,208,19,140,225,33,1,59,226,34,169,11,219,193,172,131,48,70,244,154,216,192,106,173,222,189,32,194,242,107,155,33,31,12,78,113,59,136,234,35,20,200,27,14,55,187,162,0,16,67,169,209,97,184,60,75,157,12,233,125,130,194,230,145,11,53,36,96,33,8,50,235,129,12,136,52,98,194,226,192,56,85,192,58,20,37,11,4,198,46,44,211,11,64,192,209,126,4,131,74,72,104,130,80,5,142,213,230,51,196,190,230,151,235,159,222,129,40,159,184,79,66,168,98,36,119,24,217,92,28,233,89,144,234,146,168,163,100,21,23,118,160,98,244,75,204,96,12,248,34,177,237,5,138,103,41,110,154,26,109,17,247,66,89,226,215,166,24,93,182,178,147,199,7,111,69,46,36,40,4,1,208,62,15,66,153,162,1,50,0,37,212,88,180,5,50,128,166,103,219,36,148,11,101,185,239,204,82,89,69,24,56,153,197,4,253,103,227,35,79,128,15,71,207,232,225,14,192,233,64,72,4,137,42,10,194,106,165,12,0,23,181,177,144,84,172,16,52,145,68,34,2,38,48,138,220,205,136,1,36,81,202,181,129,37,8,48,119,130,231,110,86,4,96,67,213,59,134,64,98,128,154,112,125,19,72,236,10,33,0,18,1,108,236,105,50,28,208,99,168,15,246,83,183,19,1,203,69,205,15,165,50,167,122,97,79,45,96,102,13,164,84,163,198,162,10,154,4,148,146,161,84,105,222,118,193,108,178,16,82,161,218,154,227,38,224,25,105,89,99,94,202,138,138,201,22,42,111,144,100,16,5,220,193,112,132,136,163,83,160,65,92,222,71,85,66,97,226,97,32,53,238,110,60,108,12,216,143,112,23,240,215,169,48,170,68,64,196,145,134,40,40,3,100,2,209,164,181,74,64,66,214,3,106,123,162,46,111,23,10,186,130,16,80,40,49,8,112,66,156,48,137,34,162,164,128,84,42,33,20,1,8,21,164,140,37,67,137,96,33,5,44,19,135,48,149,94,203,210,41,11,17,203,0,162,17,74,20,136,8,164,200,0,128,66,144,76,128,39,1,1,192,151,202,61,11,33,136,96,78,7,25,32,219,164,2,125,226,56,26,3,66,41,241,108,51,193,252,190,137,229,150,151,7,39,154,63,34,74,178,48,167,36,192,241,64,39,86,137,29,131,20,0,0,38,33,96,41,151,203,161,10,232,50,236,2,42,68,32,25,194,83,218,156,123,236,106,23,3,0,57,32,20,230,8,158,252,3,18,153,176,62,20,40,105,27,178,176,50,95,2,136,117,128,177,79,62,89,198,174,128,167,218,64,40,147,151,53,181,145,202,79,213,8,33,20,89,130,200,41,118,63,241,89,145,237,120,7,145,113,228,20,109,128,111,37,243,173,160,179,220,186,89,225,230,156,11,112,56,141,216,94,33,32,152,0,28,64,52,168,193,226,217,7,68,6,16,2,131,12,192,173,64,23,105,41,137,24,133,136,9,42,207,68,128,190,56,12,212,64,8,200,176,18,186,56,74,74,217,203,128,53,122,153,29,21,78,80,96,152,1,6,209,240,96,49,1,14,47,73,33,174,72,8,196,208,66,167,96,122,244,119,12,39,13,80,181,20,6,41,228,140,32,16,209,130,149,128,207,161,78,139,108,202,187,12,185,123,233,67,6,23,84,226,252,20,193,7,24,242,228,144,17,34,65,17,0,99,8,192,20,2,129,37,232,160,119,104,73,130,58,42,132,225,147,255,240,51,245,40,64,29,113,80,180,69,196,27,66,33,49,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
};
|
||||
|
||||
public static byte[] PINYIN_CODE = new byte[]
|
||||
{
|
||||
235,249,249,206,48,85,86,114,132,131,129,21,25,79,196,114,55,131,201,0,95,177,85,102,164,15,82,92,55,39,60,183,17,56,87,11,102,107,14,145,108,0,94,27,0,163,0,129,169,24,164,111,0,107,203,66,94,84,69,136,240,3,171,49,85,246,3,135,46,134,80,73,26,111,104,129,173,14,180,95,21,127,183,253,189,100,168,129,16,108,112,63,52,180,35,33,63,199,98,189,20,63,55,129,28,96,111,121,5,196,179,51,134,23,247,132,196,49,219,126,142,106,131,217,41,106,158,36,121,30,152,252,187,25,120,253,71,171,175,152,2,14,169,80,129,252,38,83,195,232,32,65,98,133,19,19,167,111,254,41,122,131,188,114,10,110,4,180,10,126,77,64,85,141,149,82,17,86,158,47,19,99,95,90,141,59,84,69,49,89,85,109,81,34,250,131,114,55,176,111,86,82,129,119,49,62,167,96,114,111,85,114,20,114,2,80,126,80,129,48,88,181,199,104,168,132,24,37,253,171,148,112,56,64,60,97,14,126,55,171,83,68,89,102,90,41,131,147,119,22,91,98,102,7,51,41,98,74,245,80,214,40,188,17,243,58,250,119,36,131,80,216,107,95,49,89,250,82,131,95,158,134,15,63,196,163,135,118,198,2,255,87,0,232,87,52,184,104,95,90,169,104,161,167,133,63,133,241,63,102,203,124,16,83,51,59,73,2,254,80,177,126,41,95,114,0,169,136,136,2,175,119,96,36,162,128,75,84,19,19,250,118,180,52,73,190,114,245,111,98,129,137,96,203,0,52,137,0,220,20,9,104,87,111,139,171,171,106,253,107,178,70,80,113,176,4,102,83,196,163,158,163,129,59,0,167,167,69,87,133,80,158,126,123,169,129,196,167,123,183,129,210,180,131,0,0,167,5,89,104,181,194,17,121,121,180,230,184,133,89,252,41,121,95,55,90,95,95,180,121,111,114,133,139,109,10,100,126,126,158,8,85,41,39,3,66,129,3,68,39,0,63,129,35,111,93,144,119,126,195,96,54,86,31,39,104,187,88,84,77,233,38,252,81,81,19,9,241,113,113,142,250,8,106,250,24,112,141,126,252,250,134,99,114,205,79,86,86,113,100,119,90,8,134,90,38,71,10,87,216,62,3,110,124,246,38,110,115,112,139,169,90,8,124,101,241,20,87,136,66,17,19,102,8,4,35,67,176,102,58,102,107,169,37,4,204,96,48,126,126,190,90,101,76,174,136,104,87,160,133,169,104,246,129,110,35,104,79,203,2,255,107,100,133,189,0,129,130,210,49,250,92,174,3,34,109,130,73,35,7,77,112,41,96,87,37,131,90,87,129,84,90,169,164,14,41,112,110,134,58,100,68,65,73,126,83,133,50,85,139,196,250,250,111,142,161,195,253,49,85,182,169,67,66,61,159,93,112,111,9,85,241,80,75,104,112,52,5,252,19,216,1,80,172,109,78,70,77,8,143,79,24,101,107,109,32,0,161,148,129,90,68,188,95,197,111,134,39,96,125,100,15,16,80,90,112,193,80,90,18,90,110,110,191,86,107,39,39,60,136,145,64,90,112,158,195,161,72,121,3,67,169,103,114,139,208,179,46,15,107,132,74,164,236,49,85,85,85,112,82,171,51,8,15,15,129,84,94,112,0,104,14,122,60,104,72,207,115,121,85,187,201,104,81,107,63,111,25,0,0,189,0,33,39,26,38,63,5,180,80,133,60,36,243,4,241,111,131,183,179,0,134,106,132,43,81,133,78,129,129,187,187,109,173,191,85,49,169,189,89,0,127,190,88,203,144,49,3,62,133,240,240,133,4,17,173,235,107,85,129,139,125,85,165,141,141,126,106,32,133,32,10,89,84,39,98,49,0,37,59,169,90,32,104,136,56,32,250,144,88,35,183,0,107,31,169,120,96,171,136,120,90,190,90,250,15,131,195,114,17,128,143,134,18,115,199,83,167,134,161,196,169,46,169,3,220,180,72,89,136,178,169,178,87,103,241,9,102,140,107,0,85,24,4,5,72,24,189,4,134,136,124,97,125,229,238,229,91,134,203,80,199,199,39,92,100,145,112,0,176,96,41,87,69,178,134,87,99,121,80,60,39,80,60,142,118,4,104,104,21,93,111,16,30,104,32,36,90,108,86,43,134,206,134,58,14,134,134,90,250,90,8,133,12,0,135,142,112,193,31,5,219,126,8,130,75,176,57,10,237,102,151,14,34,59,139,115,97,94,131,104,233,20,63,76,90,108,133,133,8,235,104,191,0,206,152,96,32,4,11,111,85,0,20,0,90,104,38,153,12,132,130,54,203,134,80,96,17,136,107,106,23,174,0,0,140,59,0,159,149,219,41,130,159,37,169,140,37,248,96,104,111,36,177,3,7,129,104,21,140,46,30,7,169,171,171,153,131,89,59,14,108,61,112,176,38,33,40,76,16,172,33,238,238,85,112,183,109,100,72,64,133,139,250,129,133,103,149,0,64,39,90,39,90,15,67,159,73,94,94,115,112,10,10,90,111,72,100,126,11,41,184,0,0,114,57,101,56,123,15,112,17,207,137,56,8,74,38,25,219,107,134,252,123,153,171,163,169,129,39,184,104,67,90,87,80,16,26,129,253,28,10,88,0,249,131,139,131,149,129,58,115,83,20,88,91,17,10,189,163,5,76,126,26,54,224,32,171,14,135,59,41,59,14,163,249,17,133,0,41,76,39,7,90,20,36,162,178,85,87,163,129,57,0,117,39,131,235,104,0,99,85,85,102,87,59,0,0,90,6,115,129,129,131,105,15,100,107,141,242,191,196,196,199,39,49,126,183,141,203,66,171,102,133,142,85,32,80,85,65,111,163,77,72,0,236,129,201,246,101,4,126,135,105,62,131,183,4,131,80,141,4,183,163,37,63,105,131,105,100,107,196,39,32,163,141,129,85,63,65,111,101,135,4,133,138,135,51,141,133,34,129,102,58,149,63,33,122,5,96,133,134,107,100,41,134,124,3,92,52,216,129,180,133,136,14,92,121,33,100,133,97,41,85,104,169,141,85,100,85,100,100,68,90,3,3,133,119,129,83,98,66,119,30,61,95,134,104,12,98,106,32,250,75,129,98,106,77,86,99,102,50,37,106,91,141,89,211,36,110,133,93,84,91,34,35,87,22,142,95,104,104,52,38,115,37,115,95,131,136,132,101,144,144,126,102,90,131,67,176,63,101,169,210,235,135,93,39,139,5,124,75,104,4,128,246,Line truncated
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
using System;
|
||||
|
||||
namespace TinyPinyin.Data
|
||||
{
|
||||
public static class PinyinData
|
||||
{
|
||||
/// <summary>
|
||||
/// 汉字 Unicode 编码最小值
|
||||
/// </summary>
|
||||
public static char MIN_VALUE = (char)19968;
|
||||
|
||||
/// <summary>
|
||||
/// 汉字 Unicode 编码最小值
|
||||
/// </summary>
|
||||
public static char MAX_VALUE = (char)40869;
|
||||
|
||||
/// <summary>
|
||||
/// 特殊的汉字“〇”,编码为 12295
|
||||
/// </summary>
|
||||
public static String PINYIN_12295 = "LING";
|
||||
public static char CHAR_12295 = (char)12295;
|
||||
|
||||
public static int PINYIN_CODE_1_OFFSET = 7000;
|
||||
|
||||
public static int PINYIN_CODE_2_OFFSET = 7000 * 2;
|
||||
|
||||
public static int[] BIT_MASKS = new int[] { 1, 2, 4, 8, 16, 32, 64, 128 };
|
||||
|
||||
public static short PADDING_MASK = 256;
|
||||
|
||||
//CHECKSTYLE:OFF
|
||||
public static string[] PINYIN_TABLE = new string[]{"", "A", "AI", "AN", "ANG", "AO", "BA", "BAI",
|
||||
"BAN", "BANG", "BAO", "BEI", "BEN", "BENG", "BI", "BIAN", "BIAO", "BIE", "BIN", "BING",
|
||||
"BO", "BU", "CA", "CAI", "CAN", "CANG", "CAO", "CE", "CEN", "CENG", "CHA", "CHAI",
|
||||
"CHAN", "CHANG", "CHAO", "CHE", "CHEN", "CHENG", "CHI", "CHONG", "CHOU", "CHU", "CHUAI",
|
||||
"CHUAN", "CHUANG", "CHUI", "CHUN", "CHUO", "CI", "CONG", "COU", "CU", "CUAN", "CUI",
|
||||
"CUN", "CUO", "DA", "DAI", "DAN", "DANG", "DAO", "DE", "DENG", "DI", "DIA", "DIAN",
|
||||
"DIAO", "DIE", "DING", "DIU", "DONG", "DOU", "DU", "DUAN", "DUI", "DUN", "DUO", "E",
|
||||
"EI", "EN", "ER", "E^", "FA", "FAN", "FANG", "FEI", "FEN", "FENG", "FO", "FOU", "FU",
|
||||
"GA", "GAI", "GAN", "GANG", "GAO", "GE", "GEI", "GEN", "GENG", "GONG", "GOU", "GU",
|
||||
"GUA", "GUAI", "GUAN", "GUANG", "GUI", "GUN", "GUO", "HA", "HAI", "HAN", "HANG", "HAO",
|
||||
"HE", "HEI", "HEN", "HENG", "HONG", "HOU", "HU", "HUA", "HUAI", "HUAN", "HUANG", "HUI",
|
||||
"HUN", "HUO", "JI", "JIA", "JIAN", "JIANG", "JIAO", "JIE", "JIN", "JING", "JIONG",
|
||||
"JIU", "JU", "JUAN", "JUE", "JUN", "KA", "KAI", "KAN", "KANG", "KAO", "KE", "KEN",
|
||||
"KENG", "KONG", "KOU", "KU", "KUA", "KUAI", "KUAN", "KUANG", "KUI", "KUN", "KUO", "LA",
|
||||
"LAI", "LAN", "LANG", "LAO", "LE", "LEI", "LENG", "LI", "LIA", "LIAN", "LIANG", "LIAO",
|
||||
"LIE", "LIN", "LING", "LIU", "LONG", "LOU", "LU", "LUAN", "LUN", "LUO", "LV", "LVE",
|
||||
"M", "MA", "MAI", "MAN", "MANG", "MAO", "ME", "MEI", "MEN", "MENG", "MI", "MIAN",
|
||||
"MIAO", "MIE", "MIN", "MING", "MIU", "MO", "MOU", "MU", "NA", "NAI", "NAN", "NANG",
|
||||
"NAO", "NE", "NEI", "NEN", "NENG", "NG", "NI", "NIAN", "NIANG", "NIAO", "NIE", "NIN",
|
||||
"NING", "NIU", "NONG", "NOU", "NU", "NUAN", "NUO", "NV", "NVE", "O", "OU", "PA", "PAI",
|
||||
"PAN", "PANG", "PAO", "PEI", "PEN", "PENG", "PI", "PIAN", "PIAO", "PIE", "PIN", "PING",
|
||||
"PO", "POU", "PU", "QI", "QIA", "QIAN", "QIANG", "QIAO", "QIE", "QIN", "QING", "QIONG",
|
||||
"QIU", "QU", "QUAN", "QUE", "QUN", "RAN", "RANG", "RAO", "RE", "REN", "RENG", "RI",
|
||||
"RONG", "ROU", "RU", "RUAN", "RUI", "RUN", "RUO", "SA", "SAI", "SAN", "SANG", "SAO",
|
||||
"SE", "SEN", "SENG", "SHA", "SHAI", "SHAN", "SHANG", "SHAO", "SHE", "SHEI", "SHEN",
|
||||
"SHENG", "SHI", "SHOU", "SHU", "SHUA", "SHUAI", "SHUAN", "SHUANG", "SHUI", "SHUN",
|
||||
"SHUO", "SI", "SONG", "SOU", "SU", "SUAN", "SUI", "SUN", "SUO", "TA", "TAI", "TAN",
|
||||
"TANG", "TAO", "TE", "TENG", "TI", "TIAN", "TIAO", "TIE", "TING", "TONG", "TOU", "TU",
|
||||
"TUAN", "TUI", "TUN", "TUO", "WA", "WAI", "WAN", "WANG", "WEI", "WEN", "WENG", "WO",
|
||||
"WU", "XI", "XIA", "XIAN", "XIANG", "XIAO", "XIE", "XIN", "XING", "XIONG", "XIU", "XU",
|
||||
"XUAN", "XUE", "XUN", "YA", "YAN", "YANG", "YAO", "YE", "YI", "YIAO", "YIN", "YING",
|
||||
"YO", "YONG", "YOU", "YU", "YUAN", "YUE", "YUN", "ZA", "ZAI", "ZAN", "ZANG", "ZAO",
|
||||
"ZE", "ZEI", "ZEN", "ZENG", "ZHA", "ZHAI", "ZHAN", "ZHANG", "ZHAO", "ZHE", "ZHEI",
|
||||
"ZHEN", "ZHENG", "ZHI", "ZHONG", "ZHOU", "ZHU", "ZHUA", "ZHUAI", "ZHUAN", "ZHUANG",
|
||||
"ZHUI", "ZHUN", "ZHUO", "ZI", "ZONG", "ZOU", "ZU", "ZUAN", "ZUI", "ZUN", "ZUO"};
|
||||
//CHECKSTYLE:ON
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using TinyPinyin.Data;
|
||||
|
||||
namespace TinyPinyin
|
||||
{
|
||||
public static class Engine
|
||||
{
|
||||
public static string[] PinyinFromDict(string wordInDict, List<IPinyinDict> pinyinDictSet)
|
||||
{
|
||||
if (pinyinDictSet != null)
|
||||
{
|
||||
foreach (IPinyinDict dict in pinyinDictSet)
|
||||
{
|
||||
if (dict != null && dict.Words() != null && dict.Words().Contains(wordInDict))
|
||||
{
|
||||
return dict.ToPinyin(wordInDict);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new ArgumentException("No pinyin dict contains word: " + wordInDict);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取给定的字符串的拼音
|
||||
/// </summary>
|
||||
/// <param name="inputStr"></param>
|
||||
/// <param name="trie"></param>
|
||||
/// <param name="pinyinDictList"></param>
|
||||
/// <param name="separator"></param>
|
||||
/// <returns></returns>
|
||||
public static string ToPinyin(string inputStr, string trie, List<IPinyinDict> pinyinDictList, string separator)
|
||||
{
|
||||
if (inputStr == null || inputStr.Length == 0)
|
||||
{
|
||||
return inputStr;
|
||||
}
|
||||
|
||||
|
||||
if (trie == null)
|
||||
{
|
||||
// 没有提供字典或选择器,按单字符转换输出
|
||||
var builder1 = new StringBuilder();
|
||||
for (int i = 0; i < inputStr.Length; i++)
|
||||
{
|
||||
builder1.Append(GetPinyinByChar(inputStr[i]));
|
||||
if (i != inputStr.Length - 1)
|
||||
{
|
||||
builder1.Append(separator);
|
||||
}
|
||||
}
|
||||
return builder1.ToString();
|
||||
}
|
||||
return null;
|
||||
|
||||
//List<Emit> selectedEmits = selector.select(trie.parseText(inputStr));
|
||||
|
||||
//Collections.sort(selectedEmits, EMIT_COMPARATOR);
|
||||
|
||||
//StringBuffer resultPinyinStrBuf = new StringBuffer();
|
||||
|
||||
//int nextHitIndex = 0;
|
||||
|
||||
//for (int i = 0; i < inputStr.length();)
|
||||
//{
|
||||
// // 首先确认是否有以第i个字符作为begin的hit
|
||||
// if (nextHitIndex < selectedEmits.size() && i == selectedEmits.get(nextHitIndex).getStart())
|
||||
// {
|
||||
// // 有以第i个字符作为begin的hit
|
||||
// String[] fromDicts = pinyinFromDict(selectedEmits.get(nextHitIndex).getKeyword(), pinyinDictList);
|
||||
// for (int j = 0; j < fromDicts.length; j++)
|
||||
// {
|
||||
// resultPinyinStrBuf.append(fromDicts[j].toUpperCase());
|
||||
// if (j != fromDicts.length - 1)
|
||||
// {
|
||||
// resultPinyinStrBuf.append(separator);
|
||||
// }
|
||||
// }
|
||||
|
||||
// i = i + selectedEmits.get(nextHitIndex).size();
|
||||
// nextHitIndex++;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// // 将第i个字符转为拼音
|
||||
// resultPinyinStrBuf.append(Pinyin.toPinyin(inputStr.charAt(i)));
|
||||
// i++;
|
||||
// }
|
||||
|
||||
// if (i != inputStr.length())
|
||||
// {
|
||||
// resultPinyinStrBuf.append(separator);
|
||||
// }
|
||||
//}
|
||||
|
||||
//return resultPinyinStrBuf.toString();
|
||||
}
|
||||
|
||||
private static int GetPinyinCode(char c)
|
||||
{
|
||||
int offset = c - PinyinData.MIN_VALUE;
|
||||
if (0 <= offset && offset < PinyinData.PINYIN_CODE_1_OFFSET)
|
||||
{
|
||||
// 前 7000 个汉字
|
||||
return decodeIndex(PinyinCode1.PINYIN_CODE_PADDING, PinyinCode1.PINYIN_CODE, offset);
|
||||
}
|
||||
else if (PinyinData.PINYIN_CODE_1_OFFSET <= offset && offset < PinyinData.PINYIN_CODE_2_OFFSET)
|
||||
{
|
||||
// 7000 ~ 14000 个汉字
|
||||
return decodeIndex(PinyinCode2.PINYIN_CODE_PADDING, PinyinCode2.PINYIN_CODE, offset - PinyinData.PINYIN_CODE_1_OFFSET);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 14000 之后的汉字
|
||||
return decodeIndex(PinyinCode3.PINYIN_CODE_PADDING, PinyinCode3.PINYIN_CODE, offset - PinyinData.PINYIN_CODE_2_OFFSET);
|
||||
}
|
||||
}
|
||||
|
||||
private static short decodeIndex(byte[] paddings, byte[] indexes, int offset)
|
||||
{
|
||||
//CHECKSTYLE:OFF
|
||||
int index1 = offset / 8;
|
||||
int index2 = offset % 8;
|
||||
short realIndex;
|
||||
realIndex = (short)(indexes[offset] & 0xff);
|
||||
//CHECKSTYLE:ON
|
||||
if ((paddings[index1] & PinyinData.BIT_MASKS[index2]) != 0)
|
||||
{
|
||||
realIndex = (short)(realIndex | PinyinData.PADDING_MASK);
|
||||
}
|
||||
return realIndex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断是否是汉字
|
||||
/// </summary>
|
||||
/// <param name="c"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsChinese(char c)
|
||||
{
|
||||
return (PinyinData.MIN_VALUE <= c && c <= PinyinData.MAX_VALUE && GetPinyinCode(c) > 0) || PinyinData.CHAR_12295 == c;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取单个汉字的完整拼音
|
||||
/// </summary>
|
||||
/// <param name="c"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetPinyinByChar(char c)
|
||||
{
|
||||
if (IsChinese(c))
|
||||
{
|
||||
if (c == PinyinData.CHAR_12295)
|
||||
{
|
||||
return PinyinData.PINYIN_12295;
|
||||
}
|
||||
else
|
||||
{
|
||||
return PinyinData.PINYIN_TABLE[GetPinyinCode(c)];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return c.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TinyPinyin
|
||||
{
|
||||
public interface IPinyinDict
|
||||
{
|
||||
/// <summary>
|
||||
/// 转换文本为到拼音
|
||||
/// </summary>
|
||||
/// <param name="word"></param>
|
||||
/// <returns></returns>
|
||||
string[] ToPinyin(string word);
|
||||
|
||||
List<string> Words();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace TinyPinyin
|
||||
{
|
||||
public abstract class PinyinDict : IPinyinDict
|
||||
{
|
||||
public abstract Dictionary<string, string[]> Mapping();
|
||||
|
||||
public string[] ToPinyin(string word)
|
||||
{
|
||||
var mappingResult = Mapping();
|
||||
if (mappingResult == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (mappingResult.TryGetValue(word, out var result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<string> Words()
|
||||
{
|
||||
var mappingResult = Mapping();
|
||||
if (mappingResult == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return mappingResult.Keys.Select(key => key).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace TinyPinyin
|
||||
{
|
||||
public static class PinyinHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 判断给定字符是否是中文
|
||||
/// </summary>
|
||||
/// <param name="c"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsChinese(char c)
|
||||
{
|
||||
return Engine.IsChinese(c);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取单个字符的拼音
|
||||
/// </summary>
|
||||
/// <param name="c"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetPinyin(char c)
|
||||
{
|
||||
return Engine.GetPinyinByChar(c);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取文本的拼音
|
||||
/// </summary>
|
||||
/// <param name="str">要获取拼音的文本</param>
|
||||
/// <param name="separator">单个拼音分隔符</param>
|
||||
/// <returns></returns>
|
||||
public static string GetPinyin(string str, string separator = " ")
|
||||
{
|
||||
return Engine.ToPinyin(str, null, null, separator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取拼音首字母
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <param name="separator">拼音分割符,默认空字符串(不分割)</param>
|
||||
/// <returns></returns>
|
||||
public static string GetPinyinInitials(string str, string separator = "")
|
||||
{
|
||||
var result = GetPinyin(str, "|");
|
||||
// 修复:获取首字母时字符串中含有|字符会报超出索引范围,https://github.com/hstarorg/TinyPinyin.Net/issues/5
|
||||
return string.Join(separator, result.Split('|').Select(x => !string.IsNullOrWhiteSpace(x) && x.Length > 0 ? x.Substring(0, 1) : x).ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,315 @@
|
||||
<UserControl x:Class="_29WxOrderWpf.Ctrls.Commons.ShopFilterComboBox"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
mc:Ignorable="d"
|
||||
x:Name="Root"
|
||||
MinHeight="30"
|
||||
KeyDown="Root_KeyDown"
|
||||
d:DesignWidth="220"
|
||||
d:DesignHeight="32">
|
||||
|
||||
<UserControl.Resources>
|
||||
<!-- 下拉按钮外观:与 BaseComboBox 类似的白底圆角 + 右侧箭头 -->
|
||||
<ControlTemplate x:Key="DropDownToggleTemplate" TargetType="ToggleButton">
|
||||
<Border x:Name="bd"
|
||||
Background="White"
|
||||
BorderBrush="#FFACACAC"
|
||||
BorderThickness="1"
|
||||
CornerRadius="4"
|
||||
SnapsToDevicePixels="True">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0"
|
||||
Text="{Binding HeaderText}"
|
||||
Margin="10,0,6,0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Foreground="#FF333333"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
ToolTip="{Binding HeaderText}" />
|
||||
<Path x:Name="ArrowPath"
|
||||
Grid.Column="1"
|
||||
Margin="0,0,10,0"
|
||||
VerticalAlignment="Center"
|
||||
Data="M 0 0 L 4 4 L 8 0"
|
||||
Stroke="#FF606060"
|
||||
StrokeThickness="1.5"
|
||||
Fill="White">
|
||||
<Path.RenderTransform>
|
||||
<RotateTransform Angle="0" CenterX="4" CenterY="2" />
|
||||
</Path.RenderTransform>
|
||||
</Path>
|
||||
</Grid>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="bd" Property="BorderBrush" Value="#FF7EB4EA" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsChecked" Value="True">
|
||||
<Setter TargetName="bd" Property="BorderBrush" Value="#FF569DE5" />
|
||||
<Setter TargetName="bd" Property="Background" Value="#FFF5F9FF" />
|
||||
<Setter TargetName="ArrowPath" Property="RenderTransform">
|
||||
<Setter.Value>
|
||||
<RotateTransform Angle="180" CenterX="4" CenterY="2" />
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter TargetName="bd" Property="Background" Value="#FFF0F0F0" />
|
||||
<Setter TargetName="bd" Property="BorderBrush" Value="#FFD9D9D9" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</UserControl.Resources>
|
||||
|
||||
<Grid x:Name="RootGrid">
|
||||
<ToggleButton x:Name="DropDownToggle"
|
||||
Focusable="True"
|
||||
Template="{StaticResource DropDownToggleTemplate}" />
|
||||
|
||||
<Popup x:Name="DropDownPopup"
|
||||
IsOpen="{Binding IsChecked, ElementName=DropDownToggle, Mode=TwoWay}"
|
||||
StaysOpen="False"
|
||||
AllowsTransparency="True"
|
||||
Placement="Bottom"
|
||||
PlacementTarget="{Binding ElementName=RootGrid}"
|
||||
VerticalOffset="4"
|
||||
PopupAnimation="Fade"
|
||||
Opened="DropDownPopup_Opened">
|
||||
|
||||
<Border x:Name="DropDownRoot"
|
||||
Background="White"
|
||||
BorderBrush="#FFBFBFBF"
|
||||
BorderThickness="1"
|
||||
CornerRadius="6"
|
||||
MaxWidth="420"
|
||||
Focusable="True"
|
||||
PreviewKeyDown="DropDownRoot_PreviewKeyDown">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect BlurRadius="12" ShadowDepth="3" Opacity="0.18" Color="#000000" />
|
||||
</Border.Effect>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 搜索框 -->
|
||||
<Border Grid.Row="0" Padding="10,10,10,6">
|
||||
<Border.Style>
|
||||
<Style TargetType="Border">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsSearchable}" Value="True">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Border.Style>
|
||||
<Grid>
|
||||
<TextBox x:Name="SearchTextBox"
|
||||
Padding="8,6"
|
||||
FontSize="13"
|
||||
VerticalContentAlignment="Center"
|
||||
BorderBrush="#FFD0D0D0"
|
||||
BorderThickness="1"
|
||||
TextChanged="SearchTextBox_TextChanged" />
|
||||
<TextBlock x:Name="SearchWaterMark"
|
||||
Text="{Binding SearchPlaceholder}"
|
||||
Margin="10,0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="13"
|
||||
Foreground="#FF999999"
|
||||
IsHitTestVisible="False">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Text, ElementName=SearchTextBox}" Value="">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- 全部商铺 -->
|
||||
<Border Grid.Row="1" Padding="10,4" Background="#FFFAFAFA" BorderBrush="#FFEEEEEE" BorderThickness="0,1,0,1">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<CheckBox x:Name="SelectAllCheckBox"
|
||||
Content="{Binding SelectAllText}"
|
||||
FontSize="13"
|
||||
FontWeight="Bold"
|
||||
VerticalAlignment="Center"
|
||||
IsThreeState="True"
|
||||
Click="SelectAllCheckBox_Click" />
|
||||
<TextBlock Grid.Column="1"
|
||||
Text="{Binding TotalCountText}"
|
||||
Margin="8,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="12"
|
||||
Foreground="#FF888888" />
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- 分组列表 -->
|
||||
<ScrollViewer Grid.Row="2"
|
||||
MaxHeight="{Binding MaxDropDownHeight}"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
HorizontalScrollBarVisibility="Disabled">
|
||||
<ItemsControl ItemsSource="{Binding DropDownGroups}" Margin="0,2,0,2">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel>
|
||||
<!-- 分组头 -->
|
||||
<Border x:Name="GroupHeaderBorder"
|
||||
Padding="10,5"
|
||||
Background="#FFF7F7F7"
|
||||
Cursor="Hand"
|
||||
MouseLeftButtonDown="GroupHeader_MouseLeftButtonDown">
|
||||
<Border.Style>
|
||||
<Style TargetType="Border">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsHeaderVisible}" Value="True">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Border.Style>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Margin="0,0,6,0"
|
||||
FontSize="10"
|
||||
Foreground="#FF666666">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Text" Value="▶" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsExpanded}" Value="True">
|
||||
<Setter Property="Text" Value="▼" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
<CheckBox Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0,0,8,0"
|
||||
IsThreeState="True"
|
||||
IsChecked="{Binding IsChecked}" />
|
||||
<TextBlock Grid.Column="2"
|
||||
Text="{Binding Name}"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="13"
|
||||
FontWeight="Bold"
|
||||
Foreground="#FF333333" />
|
||||
<TextBlock Grid.Column="3"
|
||||
Text="{Binding CountText}"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="12"
|
||||
Foreground="#FF888888" />
|
||||
</Grid>
|
||||
</Border>
|
||||
<!-- 分组项 -->
|
||||
<ItemsControl ItemsSource="{Binding Items}">
|
||||
<ItemsControl.Style>
|
||||
<Style TargetType="ItemsControl">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsExpanded}" Value="True">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ItemsControl.Style>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid Margin="30,1,10,1">
|
||||
<Grid.Style>
|
||||
<Style TargetType="Grid">
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsHighlighted}" Value="True">
|
||||
<Setter Property="Background" Value="#FFE3F0FF" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Grid.Style>
|
||||
<CheckBox Content="{Binding DisplayName}"
|
||||
IsChecked="{Binding IsSelected}"
|
||||
Padding="4,3"
|
||||
FontSize="13"
|
||||
VerticalContentAlignment="Center"
|
||||
Foreground="#FF333333"
|
||||
Click="ItemCheckBox_Click" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
|
||||
<!-- 底部:已选择 + 清空/全选/确定 -->
|
||||
<Border Grid.Row="3" Padding="10,8" Background="#FFFAFAFA" BorderBrush="#FFEEEEEE" BorderThickness="0,1,0,0">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="{Binding SelectedCountText}"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="13"
|
||||
Foreground="#FF555555" />
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal">
|
||||
<Button Content="清空"
|
||||
Padding="12,4"
|
||||
Margin="4,0,0,0"
|
||||
FontSize="13"
|
||||
Style="{StaticResource BasePageButtonLightStyle}"
|
||||
Click="ClearButton_Click" />
|
||||
<Button Content="全选"
|
||||
Padding="12,4"
|
||||
Margin="8,0,0,0"
|
||||
FontSize="13"
|
||||
Style="{StaticResource BasePageButtonLightStyle}"
|
||||
Click="SelectAllButton_Click" />
|
||||
<Button Content="确定"
|
||||
Padding="14,4"
|
||||
Margin="8,0,0,0"
|
||||
FontSize="13"
|
||||
Style="{StaticResource BasePageButtonStyle}"
|
||||
Click="ConfirmButton_Click" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Popup>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,869 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using TinyPinyin;
|
||||
|
||||
namespace _29WxOrderWpf.Ctrls.Commons
|
||||
{
|
||||
/// <summary>
|
||||
/// 商铺筛选 ComboBox 控件。
|
||||
/// 只需绑定:
|
||||
/// ItemsSource 数据源(如 ObservableCollection<ShopOption>)
|
||||
/// SelectedItems 选中的项集合(TwoWay,如 ObservableCollection<ShopOption>)
|
||||
/// DisplayMemberPath 显示名称的属性路径(如 ShopName)
|
||||
/// IsSearchable 是否显示搜索框(默认 true)
|
||||
/// IsGrouped 是否按分组显示(默认 false)
|
||||
/// GroupByPath 分组名称的属性路径(IsGrouped=true 时生效,如 Region)
|
||||
/// 搜索支持中文、全拼、拼音首字母;键盘:
|
||||
/// ↑/↓ 选择(移动高亮),空格 选中/取消,Enter 确定,Esc 取消(还原打开前的选择)。
|
||||
/// </summary>
|
||||
public partial class ShopFilterComboBox : UserControl, INotifyPropertyChanged
|
||||
{
|
||||
private readonly ObservableCollection<ShopFilterGroup> _groups = new ObservableCollection<ShopFilterGroup>();
|
||||
private readonly List<ShopFilterGroup> _allGroups = new List<ShopFilterGroup>();
|
||||
private readonly List<ShopFilterItem> _allItems = new List<ShopFilterItem>();
|
||||
private readonly List<ShopFilterItem> _visibleItems = new List<ShopFilterItem>();
|
||||
private ShopFilterItem _highlightedItem;
|
||||
private List<object> _openedSnapshot;
|
||||
private bool _syncing;
|
||||
private INotifyCollectionChanged _itemsNotifier;
|
||||
private INotifyCollectionChanged _selectedNotifier;
|
||||
|
||||
public ShopFilterComboBox()
|
||||
{
|
||||
InitializeComponent();
|
||||
// 内部 UI 统一绑定到控件自身(外部 ItemsSource/SelectedItems 等仍绑定在控件上,不受影响)
|
||||
RootGrid.DataContext = this;
|
||||
DropDownRoot.DataContext = this;
|
||||
SelectedItems = new ObservableCollection<object>();
|
||||
}
|
||||
|
||||
#region 依赖属性
|
||||
|
||||
public static readonly DependencyProperty ItemsSourceProperty =
|
||||
DependencyProperty.Register(nameof(ItemsSource), typeof(IEnumerable), typeof(ShopFilterComboBox),
|
||||
new PropertyMetadata(null, OnItemsSourceChanged));
|
||||
|
||||
/// <summary>数据源</summary>
|
||||
public IEnumerable ItemsSource
|
||||
{
|
||||
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
|
||||
set { SetValue(ItemsSourceProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty SelectedItemsProperty =
|
||||
DependencyProperty.Register(nameof(SelectedItems), typeof(IList), typeof(ShopFilterComboBox),
|
||||
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedItemsChanged));
|
||||
|
||||
/// <summary>选中的项集合(双向绑定,元素为 ItemsSource 中的原始对象)</summary>
|
||||
public IList SelectedItems
|
||||
{
|
||||
get { return (IList)GetValue(SelectedItemsProperty); }
|
||||
set { SetValue(SelectedItemsProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty DisplayMemberPathProperty =
|
||||
DependencyProperty.Register(nameof(DisplayMemberPath), typeof(string), typeof(ShopFilterComboBox),
|
||||
new PropertyMetadata(null, OnViewPropertyChanged));
|
||||
|
||||
/// <summary>显示名称的属性路径(如 ShopName)</summary>
|
||||
public string DisplayMemberPath
|
||||
{
|
||||
get { return (string)GetValue(DisplayMemberPathProperty); }
|
||||
set { SetValue(DisplayMemberPathProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IsSearchableProperty =
|
||||
DependencyProperty.Register(nameof(IsSearchable), typeof(bool), typeof(ShopFilterComboBox),
|
||||
new PropertyMetadata(true));
|
||||
|
||||
/// <summary>是否显示搜索框</summary>
|
||||
public bool IsSearchable
|
||||
{
|
||||
get { return (bool)GetValue(IsSearchableProperty); }
|
||||
set { SetValue(IsSearchableProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IsGroupedProperty =
|
||||
DependencyProperty.Register(nameof(IsGrouped), typeof(bool), typeof(ShopFilterComboBox),
|
||||
new PropertyMetadata(false, OnViewPropertyChanged));
|
||||
|
||||
/// <summary>是否按分组显示</summary>
|
||||
public bool IsGrouped
|
||||
{
|
||||
get { return (bool)GetValue(IsGroupedProperty); }
|
||||
set { SetValue(IsGroupedProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty GroupByPathProperty =
|
||||
DependencyProperty.Register(nameof(GroupByPath), typeof(string), typeof(ShopFilterComboBox),
|
||||
new PropertyMetadata(null, OnViewPropertyChanged));
|
||||
|
||||
/// <summary>分组名称的属性路径(IsGrouped=true 时生效,如 Region)</summary>
|
||||
public string GroupByPath
|
||||
{
|
||||
get { return (string)GetValue(GroupByPathProperty); }
|
||||
set { SetValue(GroupByPathProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty AllTextProperty =
|
||||
DependencyProperty.Register(nameof(AllText), typeof(string), typeof(ShopFilterComboBox),
|
||||
new PropertyMetadata("全部商铺"));
|
||||
|
||||
/// <summary>未选择或全部选择时头部显示的文本(默认“全部商铺”)</summary>
|
||||
public string AllText
|
||||
{
|
||||
get { return (string)GetValue(AllTextProperty); }
|
||||
set { SetValue(AllTextProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty UnitTextProperty =
|
||||
DependencyProperty.Register(nameof(UnitText), typeof(string), typeof(ShopFilterComboBox),
|
||||
new PropertyMetadata("个商铺"));
|
||||
|
||||
/// <summary>头部数量单位(默认“个商铺”,显示如“8 个商铺”)</summary>
|
||||
public string UnitText
|
||||
{
|
||||
get { return (string)GetValue(UnitTextProperty); }
|
||||
set { SetValue(UnitTextProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty CountSuffixProperty =
|
||||
DependencyProperty.Register(nameof(CountSuffix), typeof(string), typeof(ShopFilterComboBox),
|
||||
new PropertyMetadata("家", OnViewPropertyChanged));
|
||||
|
||||
/// <summary>数量单位后缀(默认“家”,显示如“共28家”“12家”“已选择:8 家”)</summary>
|
||||
public string CountSuffix
|
||||
{
|
||||
get { return (string)GetValue(CountSuffixProperty); }
|
||||
set { SetValue(CountSuffixProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty SelectAllTextProperty =
|
||||
DependencyProperty.Register(nameof(SelectAllText), typeof(string), typeof(ShopFilterComboBox),
|
||||
new PropertyMetadata("全部商铺"));
|
||||
|
||||
/// <summary>“全部”复选框文案(默认“全部商铺”)</summary>
|
||||
public string SelectAllText
|
||||
{
|
||||
get { return (string)GetValue(SelectAllTextProperty); }
|
||||
set { SetValue(SelectAllTextProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty SearchPlaceholderProperty =
|
||||
DependencyProperty.Register(nameof(SearchPlaceholder), typeof(string), typeof(ShopFilterComboBox),
|
||||
new PropertyMetadata("搜索商铺..."));
|
||||
|
||||
/// <summary>搜索框占位文案</summary>
|
||||
public string SearchPlaceholder
|
||||
{
|
||||
get { return (string)GetValue(SearchPlaceholderProperty); }
|
||||
set { SetValue(SearchPlaceholderProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty MaxDropDownHeightProperty =
|
||||
DependencyProperty.Register(nameof(MaxDropDownHeight), typeof(double), typeof(ShopFilterComboBox),
|
||||
new PropertyMetadata(300.0));
|
||||
|
||||
/// <summary>下拉列表最大高度</summary>
|
||||
public double MaxDropDownHeight
|
||||
{
|
||||
get { return (double)GetValue(MaxDropDownHeightProperty); }
|
||||
set { SetValue(MaxDropDownHeightProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty ExpandGroupsByDefaultProperty =
|
||||
DependencyProperty.Register(nameof(ExpandGroupsByDefault), typeof(bool), typeof(ShopFilterComboBox),
|
||||
new PropertyMetadata(true));
|
||||
|
||||
/// <summary>分组默认是否展开</summary>
|
||||
public bool ExpandGroupsByDefault
|
||||
{
|
||||
get { return (bool)GetValue(ExpandGroupsByDefaultProperty); }
|
||||
set { SetValue(ExpandGroupsByDefaultProperty, value); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 路由事件
|
||||
|
||||
public static readonly RoutedEvent SelectionChangedEvent =
|
||||
EventManager.RegisterRoutedEvent("SelectionChanged", RoutingStrategy.Bubble,
|
||||
typeof(RoutedEventHandler), typeof(ShopFilterComboBox));
|
||||
|
||||
/// <summary>选中集合内容变化时触发</summary>
|
||||
public event RoutedEventHandler SelectionChanged
|
||||
{
|
||||
add { AddHandler(SelectionChangedEvent, value); }
|
||||
remove { RemoveHandler(SelectionChangedEvent, value); }
|
||||
}
|
||||
|
||||
public static readonly RoutedEvent ConfirmedEvent =
|
||||
EventManager.RegisterRoutedEvent("Confirmed", RoutingStrategy.Bubble,
|
||||
typeof(RoutedEventHandler), typeof(ShopFilterComboBox));
|
||||
|
||||
/// <summary>点击“确定”或按 Enter 时触发(可在此执行查询)</summary>
|
||||
public event RoutedEventHandler Confirmed
|
||||
{
|
||||
add { AddHandler(ConfirmedEvent, value); }
|
||||
remove { RemoveHandler(ConfirmedEvent, value); }
|
||||
}
|
||||
|
||||
public static readonly RoutedEvent CancelledEvent =
|
||||
EventManager.RegisterRoutedEvent("Cancelled", RoutingStrategy.Bubble,
|
||||
typeof(RoutedEventHandler), typeof(ShopFilterComboBox));
|
||||
|
||||
/// <summary>按 Esc 取消时触发(已还原打开前的选择)</summary>
|
||||
public event RoutedEventHandler Cancelled
|
||||
{
|
||||
add { AddHandler(CancelledEvent, value); }
|
||||
remove { RemoveHandler(CancelledEvent, value); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 内部绑定属性
|
||||
|
||||
/// <summary>当前显示的分组集合</summary>
|
||||
public ObservableCollection<ShopFilterGroup> DropDownGroups
|
||||
{
|
||||
get { return _groups; }
|
||||
}
|
||||
|
||||
/// <summary>头部文本(全部商铺 / N 个商铺)</summary>
|
||||
public string HeaderText
|
||||
{
|
||||
get
|
||||
{
|
||||
var sel = SelectedItems;
|
||||
int total = _allItems.Count;
|
||||
int count = sel == null ? 0 : sel.Count;
|
||||
if (count == 0 || (total > 0 && count >= total)) return AllText;
|
||||
return count + " " + UnitText;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>总数文本(如“共28家”)</summary>
|
||||
public string TotalCountText
|
||||
{
|
||||
get { return "共" + _allItems.Count + CountSuffix; }
|
||||
}
|
||||
|
||||
/// <summary>已选择文本(如“已选择:8 家”)</summary>
|
||||
public string SelectedCountText
|
||||
{
|
||||
get { return "已选择:" + (SelectedItems == null ? 0 : SelectedItems.Count) + " " + CountSuffix; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 依赖属性回调
|
||||
|
||||
private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var c = (ShopFilterComboBox)d;
|
||||
c.UnsubscribeItems();
|
||||
c.SubscribeItems();
|
||||
c.Rebuild();
|
||||
}
|
||||
|
||||
private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var c = (ShopFilterComboBox)d;
|
||||
c.UnsubscribeSelected();
|
||||
c.SubscribeSelected();
|
||||
c.OnSelectionContentChanged();
|
||||
}
|
||||
|
||||
private static void OnViewPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
((ShopFilterComboBox)d).Rebuild();
|
||||
}
|
||||
|
||||
private void SubscribeItems()
|
||||
{
|
||||
_itemsNotifier = ItemsSource as INotifyCollectionChanged;
|
||||
if (_itemsNotifier != null) _itemsNotifier.CollectionChanged += OnItemsSourceCollectionChanged;
|
||||
}
|
||||
|
||||
private void UnsubscribeItems()
|
||||
{
|
||||
if (_itemsNotifier != null) _itemsNotifier.CollectionChanged -= OnItemsSourceCollectionChanged;
|
||||
_itemsNotifier = null;
|
||||
}
|
||||
|
||||
private void SubscribeSelected()
|
||||
{
|
||||
_selectedNotifier = SelectedItems as INotifyCollectionChanged;
|
||||
if (_selectedNotifier != null) _selectedNotifier.CollectionChanged += OnSelectedItemsCollectionChanged;
|
||||
}
|
||||
|
||||
private void UnsubscribeSelected()
|
||||
{
|
||||
if (_selectedNotifier != null) _selectedNotifier.CollectionChanged -= OnSelectedItemsCollectionChanged;
|
||||
_selectedNotifier = null;
|
||||
}
|
||||
|
||||
private void OnItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
Rebuild();
|
||||
}
|
||||
|
||||
private void OnSelectedItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
if (_syncing) return;
|
||||
_syncing = true;
|
||||
try { SyncItemStatesFromSelection(); }
|
||||
finally { _syncing = false; }
|
||||
RefreshSelectionState();
|
||||
RaiseSelectionChanged();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 构建与过滤
|
||||
|
||||
private void Rebuild()
|
||||
{
|
||||
_allItems.Clear();
|
||||
_allGroups.Clear();
|
||||
|
||||
bool grouped = IsGrouped && !string.IsNullOrEmpty(GroupByPath);
|
||||
Dictionary<string, ShopFilterGroup> groupMap = null;
|
||||
ShopFilterGroup flatGroup = null;
|
||||
|
||||
if (grouped)
|
||||
{
|
||||
groupMap = new Dictionary<string, ShopFilterGroup>(StringComparer.Ordinal);
|
||||
}
|
||||
else
|
||||
{
|
||||
flatGroup = new ShopFilterGroup { Name = string.Empty, CountSuffix = CountSuffix, IsExpanded = true };
|
||||
_allGroups.Add(flatGroup);
|
||||
}
|
||||
|
||||
var source = ItemsSource;
|
||||
if (source != null)
|
||||
{
|
||||
foreach (var obj in source)
|
||||
{
|
||||
string name = GetDisplayText(obj, DisplayMemberPath);
|
||||
string pinyinFull = string.Empty;
|
||||
string initials = string.Empty;
|
||||
try
|
||||
{
|
||||
pinyinFull = PinyinHelper.GetPinyin(name, "").ToLowerInvariant();
|
||||
initials = PinyinHelper.GetPinyinInitials(name, "").ToLowerInvariant();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// 拼音转换失败时退化为仅名称匹配
|
||||
}
|
||||
|
||||
var item = new ShopFilterItem
|
||||
{
|
||||
Source = obj,
|
||||
DisplayName = name,
|
||||
GroupName = grouped ? GetDisplayText(obj, GroupByPath) : null,
|
||||
NameSearch = name.Replace(" ", "").ToLowerInvariant(),
|
||||
PinyinSearch = pinyinFull,
|
||||
InitialsSearch = initials
|
||||
};
|
||||
item.PropertyChanged += OnItemPropertyChanged;
|
||||
_allItems.Add(item);
|
||||
|
||||
if (grouped)
|
||||
{
|
||||
string gname = string.IsNullOrEmpty(item.GroupName) ? "未分组" : item.GroupName;
|
||||
ShopFilterGroup group;
|
||||
if (!groupMap.TryGetValue(gname, out group))
|
||||
{
|
||||
group = new ShopFilterGroup
|
||||
{
|
||||
Name = gname,
|
||||
CountSuffix = CountSuffix,
|
||||
IsExpanded = ExpandGroupsByDefault
|
||||
};
|
||||
group.PropertyChanged += OnGroupPropertyChanged;
|
||||
groupMap[gname] = group;
|
||||
_allGroups.Add(group);
|
||||
}
|
||||
group.AllItems.Add(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
flatGroup.AllItems.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_syncing = true;
|
||||
try { SyncItemStatesFromSelection(); }
|
||||
finally { _syncing = false; }
|
||||
|
||||
ApplyFilter();
|
||||
RefreshSelectionState();
|
||||
}
|
||||
|
||||
private void ApplyFilter()
|
||||
{
|
||||
string keyword = SearchTextBox == null ? string.Empty : (SearchTextBox.Text ?? string.Empty).Trim();
|
||||
bool searching = keyword.Length > 0;
|
||||
// 搜索关键词去空格并小写,可同时匹配 中文/全拼/首字母(如 “sh”“shang hai”“上海”)
|
||||
string kw = keyword.Replace(" ", "").ToLowerInvariant();
|
||||
|
||||
_groups.Clear();
|
||||
_visibleItems.Clear();
|
||||
foreach (var group in _allGroups)
|
||||
{
|
||||
group.Items.Clear();
|
||||
foreach (var item in group.AllItems)
|
||||
{
|
||||
bool match = !searching
|
||||
|| item.NameSearch.Contains(kw)
|
||||
|| item.PinyinSearch.Contains(kw)
|
||||
|| item.InitialsSearch.Contains(kw);
|
||||
if (match)
|
||||
{
|
||||
group.Items.Add(item);
|
||||
_visibleItems.Add(item);
|
||||
}
|
||||
}
|
||||
if (searching && group.Items.Count > 0)
|
||||
group.IsExpanded = true;
|
||||
group.RefreshCountText();
|
||||
if (!searching || group.Items.Count > 0)
|
||||
_groups.Add(group);
|
||||
}
|
||||
|
||||
// 高亮项不在可见列表时,重置到第一项
|
||||
if (_visibleItems.Count == 0)
|
||||
SetHighlight(null);
|
||||
else if (!_visibleItems.Contains(_highlightedItem))
|
||||
SetHighlight(_visibleItems[0]);
|
||||
}
|
||||
|
||||
private void SyncItemStatesFromSelection()
|
||||
{
|
||||
var sel = SelectedItems;
|
||||
foreach (var item in _allItems)
|
||||
{
|
||||
bool selected = sel != null && sel.Contains(item.Source);
|
||||
item.IsSelected = selected;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 选择逻辑
|
||||
|
||||
private void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName != nameof(ShopFilterItem.IsSelected)) return;
|
||||
if (_syncing) return;
|
||||
|
||||
var item = (ShopFilterItem)sender;
|
||||
if (item.IsSelected) AddToSelection(item.Source);
|
||||
else RemoveFromSelection(item.Source);
|
||||
RefreshSelectionState();
|
||||
RaiseSelectionChanged();
|
||||
}
|
||||
|
||||
private void OnGroupPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName != nameof(ShopFilterGroup.IsChecked)) return;
|
||||
if (_syncing) return;
|
||||
|
||||
var group = (ShopFilterGroup)sender;
|
||||
bool select = group.IsChecked == true;
|
||||
_syncing = true;
|
||||
try
|
||||
{
|
||||
foreach (var item in group.AllItems)
|
||||
item.IsSelected = select;
|
||||
foreach (var item in group.AllItems)
|
||||
{
|
||||
if (select) AddToSelection(item.Source);
|
||||
else RemoveFromSelection(item.Source);
|
||||
}
|
||||
}
|
||||
finally { _syncing = false; }
|
||||
RefreshSelectionState();
|
||||
RaiseSelectionChanged();
|
||||
}
|
||||
|
||||
private void AddToSelection(object source)
|
||||
{
|
||||
var sel = SelectedItems;
|
||||
if (sel == null || source == null) return;
|
||||
if (!sel.Contains(source)) sel.Add(source);
|
||||
}
|
||||
|
||||
private void RemoveFromSelection(object source)
|
||||
{
|
||||
var sel = SelectedItems;
|
||||
if (sel == null || source == null) return;
|
||||
if (sel.Contains(source)) sel.Remove(source);
|
||||
}
|
||||
|
||||
/// <summary>全选</summary>
|
||||
public void SelectAll()
|
||||
{
|
||||
_syncing = true;
|
||||
try
|
||||
{
|
||||
foreach (var item in _allItems) item.IsSelected = true;
|
||||
foreach (var item in _allItems) AddToSelection(item.Source);
|
||||
}
|
||||
finally { _syncing = false; }
|
||||
RefreshSelectionState();
|
||||
RaiseSelectionChanged();
|
||||
}
|
||||
|
||||
/// <summary>清空选择</summary>
|
||||
public void ClearSelection()
|
||||
{
|
||||
_syncing = true;
|
||||
try
|
||||
{
|
||||
foreach (var item in _allItems) item.IsSelected = false;
|
||||
if (SelectedItems != null)
|
||||
{
|
||||
try { SelectedItems.Clear(); }
|
||||
catch (NotSupportedException) { }
|
||||
}
|
||||
}
|
||||
finally { _syncing = false; }
|
||||
RefreshSelectionState();
|
||||
RaiseSelectionChanged();
|
||||
}
|
||||
|
||||
private void RefreshSelectionState()
|
||||
{
|
||||
var sel = SelectedItems;
|
||||
int total = _allItems.Count;
|
||||
int count = sel == null ? 0 : sel.Count;
|
||||
|
||||
_syncing = true;
|
||||
try
|
||||
{
|
||||
foreach (var group in _allGroups)
|
||||
{
|
||||
int selected = group.AllItems.Count(i => i.IsSelected);
|
||||
group.IsChecked = selected == 0 ? false
|
||||
: (selected >= group.AllItems.Count ? (bool?)true : null);
|
||||
}
|
||||
if (SelectAllCheckBox != null)
|
||||
{
|
||||
SelectAllCheckBox.IsChecked = count == 0 ? false
|
||||
: (total > 0 && count >= total ? true : (bool?)null);
|
||||
}
|
||||
}
|
||||
finally { _syncing = false; }
|
||||
|
||||
OnPropertyChanged(nameof(HeaderText));
|
||||
OnPropertyChanged(nameof(TotalCountText));
|
||||
OnPropertyChanged(nameof(SelectedCountText));
|
||||
}
|
||||
|
||||
private void RaiseSelectionChanged()
|
||||
{
|
||||
RaiseEvent(new RoutedEventArgs(SelectionChangedEvent));
|
||||
}
|
||||
|
||||
/// <summary>确定:关闭下拉并触发 Confirmed</summary>
|
||||
private void Confirm()
|
||||
{
|
||||
DropDownToggle.IsChecked = false;
|
||||
RaiseEvent(new RoutedEventArgs(ConfirmedEvent));
|
||||
}
|
||||
|
||||
/// <summary>取消:还原打开前的选择,关闭下拉并触发 Cancelled</summary>
|
||||
private void Cancel()
|
||||
{
|
||||
_syncing = true;
|
||||
try
|
||||
{
|
||||
var sel = SelectedItems;
|
||||
if (sel != null)
|
||||
{
|
||||
try { sel.Clear(); }
|
||||
catch (NotSupportedException) { }
|
||||
if (_openedSnapshot != null)
|
||||
{
|
||||
foreach (var obj in _openedSnapshot)
|
||||
sel.Add(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally { _syncing = false; }
|
||||
RefreshSelectionState();
|
||||
RaiseSelectionChanged();
|
||||
DropDownToggle.IsChecked = false;
|
||||
RaiseEvent(new RoutedEventArgs(CancelledEvent));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 键盘
|
||||
|
||||
/// <summary>下拉打开时:↑/↓ 选择,空格 选中/取消,Enter 确定,Esc 取消</summary>
|
||||
private void DropDownRoot_PreviewKeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
// 输入法合成中(如拼音输入法)时,Enter/Esc 交给输入法处理,避免误触发确定/取消
|
||||
if ((e.Key == Key.Enter || e.Key == Key.Escape) && e.ImeProcessedKey != Key.None)
|
||||
return;
|
||||
|
||||
switch (e.Key)
|
||||
{
|
||||
case Key.Escape:
|
||||
e.Handled = true;
|
||||
Cancel();
|
||||
break;
|
||||
case Key.Enter:
|
||||
e.Handled = true;
|
||||
Confirm();
|
||||
break;
|
||||
case Key.Down:
|
||||
e.Handled = true;
|
||||
MoveHighlight(1);
|
||||
break;
|
||||
case Key.Up:
|
||||
e.Handled = true;
|
||||
MoveHighlight(-1);
|
||||
break;
|
||||
case Key.Space:
|
||||
if (_highlightedItem != null)
|
||||
{
|
||||
e.Handled = true;
|
||||
_highlightedItem.IsSelected = !_highlightedItem.IsSelected;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>下拉关闭时按 ↑/↓/Enter/空格 打开下拉</summary>
|
||||
private void Root_KeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (DropDownToggle.IsChecked == true) return;
|
||||
if (e.Key == Key.Down || e.Key == Key.Up || e.Key == Key.Enter || e.Key == Key.Space)
|
||||
{
|
||||
e.Handled = true;
|
||||
DropDownToggle.IsChecked = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetHighlight(ShopFilterItem item)
|
||||
{
|
||||
if (ReferenceEquals(_highlightedItem, item)) return;
|
||||
if (_highlightedItem != null) _highlightedItem.IsHighlighted = false;
|
||||
_highlightedItem = item;
|
||||
if (_highlightedItem != null)
|
||||
{
|
||||
_highlightedItem.IsHighlighted = true;
|
||||
// 高亮项所在分组若被折叠则展开
|
||||
foreach (var group in _allGroups)
|
||||
{
|
||||
if (group.AllItems.Contains(_highlightedItem) && !group.IsExpanded)
|
||||
{
|
||||
group.IsExpanded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
BringHighlightedIntoView();
|
||||
}
|
||||
}
|
||||
|
||||
private void MoveHighlight(int delta)
|
||||
{
|
||||
if (_visibleItems.Count == 0) return;
|
||||
int idx;
|
||||
if (_highlightedItem == null)
|
||||
{
|
||||
idx = delta >= 0 ? 0 : _visibleItems.Count - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
idx = _visibleItems.IndexOf(_highlightedItem);
|
||||
if (idx < 0) idx = delta >= 0 ? 0 : _visibleItems.Count - 1;
|
||||
else idx = Math.Max(0, Math.Min(_visibleItems.Count - 1, idx + delta));
|
||||
}
|
||||
SetHighlight(_visibleItems[idx]);
|
||||
}
|
||||
|
||||
private void BringHighlightedIntoView()
|
||||
{
|
||||
if (_highlightedItem == null) return;
|
||||
var check = FindVisualChild<CheckBox>(DropDownRoot, cb => cb.DataContext == _highlightedItem);
|
||||
if (check != null) check.BringIntoView();
|
||||
}
|
||||
|
||||
private static T FindVisualChild<T>(DependencyObject parent, Func<T, bool> predicate) where T : DependencyObject
|
||||
{
|
||||
int count = VisualTreeHelper.GetChildrenCount(parent);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var child = VisualTreeHelper.GetChild(parent, i);
|
||||
if (child is T t && predicate(t)) return t;
|
||||
var result = FindVisualChild(child, predicate);
|
||||
if (result != null) return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 事件处理
|
||||
|
||||
private void SearchTextBox_TextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
ApplyFilter();
|
||||
}
|
||||
|
||||
private void SelectAllCheckBox_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var cb = (CheckBox)sender;
|
||||
if (cb.IsChecked == true) SelectAll();
|
||||
else ClearSelection();
|
||||
}
|
||||
|
||||
/// <summary>鼠标点击某项时,把键盘高亮同步到该项</summary>
|
||||
private void ItemCheckBox_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var item = (sender as FrameworkElement)?.DataContext as ShopFilterItem;
|
||||
if (item != null) SetHighlight(item);
|
||||
}
|
||||
|
||||
private void ClearButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ClearSelection();
|
||||
}
|
||||
|
||||
private void SelectAllButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
SelectAll();
|
||||
}
|
||||
|
||||
private void ConfirmButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Confirm();
|
||||
}
|
||||
|
||||
private void GroupHeader_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
var group = (sender as FrameworkElement)?.DataContext as ShopFilterGroup;
|
||||
if (group != null) group.IsExpanded = !group.IsExpanded;
|
||||
}
|
||||
|
||||
private void DropDownPopup_Opened(object sender, EventArgs e)
|
||||
{
|
||||
// 下拉宽度不小于按钮宽度
|
||||
if (DropDownRoot != null)
|
||||
DropDownRoot.MinWidth = Math.Max(300, RootGrid.ActualWidth);
|
||||
|
||||
// 记录打开时的选中快照,Esc 取消时还原
|
||||
_openedSnapshot = SelectedItems == null ? new List<object>() : SelectedItems.Cast<object>().ToList();
|
||||
|
||||
// 每次打开清空搜索,显示完整列表
|
||||
if (SearchTextBox != null)
|
||||
SearchTextBox.Text = string.Empty;
|
||||
|
||||
// 重新同步外部对 SelectedItems 的修改
|
||||
OnSelectionContentChanged();
|
||||
|
||||
// 高亮第一项
|
||||
if (_visibleItems.Count > 0)
|
||||
SetHighlight(_visibleItems[0]);
|
||||
|
||||
if (SearchTextBox != null && IsSearchable)
|
||||
{
|
||||
SearchTextBox.Focus();
|
||||
SearchTextBox.SelectAll();
|
||||
}
|
||||
else
|
||||
{
|
||||
DropDownRoot.Focus();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSelectionContentChanged()
|
||||
{
|
||||
if (_syncing) return;
|
||||
_syncing = true;
|
||||
try { SyncItemStatesFromSelection(); }
|
||||
finally { _syncing = false; }
|
||||
RefreshSelectionState();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 反射取值
|
||||
|
||||
private static readonly Dictionary<Type, Dictionary<string, PropertyInfo>> _propertyCache =
|
||||
new Dictionary<Type, Dictionary<string, PropertyInfo>>();
|
||||
private static readonly object _cacheLock = new object();
|
||||
|
||||
private static PropertyInfo FindProperty(Type type, string path)
|
||||
{
|
||||
lock (_cacheLock)
|
||||
{
|
||||
Dictionary<string, PropertyInfo> map;
|
||||
if (!_propertyCache.TryGetValue(type, out map))
|
||||
{
|
||||
map = new Dictionary<string, PropertyInfo>(StringComparer.Ordinal);
|
||||
_propertyCache[type] = map;
|
||||
}
|
||||
PropertyInfo cached;
|
||||
if (map.TryGetValue(path, out cached)) return cached;
|
||||
|
||||
PropertyInfo pi = null;
|
||||
Type current = type;
|
||||
foreach (var part in path.Split('.'))
|
||||
{
|
||||
pi = current.GetProperty(part, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
|
||||
if (pi == null) break;
|
||||
current = pi.PropertyType;
|
||||
}
|
||||
map[path] = pi;
|
||||
return pi;
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetDisplayText(object item, string path)
|
||||
{
|
||||
if (item == null) return string.Empty;
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return Convert.ToString(item, CultureInfo.CurrentCulture);
|
||||
|
||||
var pi = FindProperty(item.GetType(), path);
|
||||
if (pi == null) return string.Empty;
|
||||
var value = pi.GetValue(item);
|
||||
return value == null ? string.Empty : Convert.ToString(value, CultureInfo.CurrentCulture);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region INotifyPropertyChanged
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
private void OnPropertyChanged(string propertyName)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace _29WxOrderWpf.Ctrls.Commons
|
||||
{
|
||||
/// <summary>
|
||||
/// 商铺筛选下拉 - 单项视图模型(包装原始数据项,附加显示名/分组名/选中状态/搜索键/高亮)。
|
||||
/// 由 <see cref="ShopFilterComboBox"/> 内部维护,不直接使用。
|
||||
/// </summary>
|
||||
public class ShopFilterItem : INotifyPropertyChanged
|
||||
{
|
||||
private bool _isSelected;
|
||||
private bool _isHighlighted;
|
||||
|
||||
/// <summary>原始数据项(ItemsSource 中的元素)</summary>
|
||||
public object Source { get; set; }
|
||||
|
||||
/// <summary>显示名称(由 DisplayMemberPath 解析)</summary>
|
||||
public string DisplayName { get; set; }
|
||||
|
||||
/// <summary>所属分组名称(由 GroupByPath 解析,未分组模式下为 null)</summary>
|
||||
public string GroupName { get; set; }
|
||||
|
||||
/// <summary>搜索键:名称(去空格小写,支持中文片段匹配)</summary>
|
||||
public string NameSearch { get; set; }
|
||||
|
||||
/// <summary>搜索键:完整拼音(无空格小写,如“上海旗舰店”=“shanghaiqijiandian”)</summary>
|
||||
public string PinyinSearch { get; set; }
|
||||
|
||||
/// <summary>搜索键:拼音首字母(小写,如“上海旗舰店”=“shqjd”)</summary>
|
||||
public string InitialsSearch { get; set; }
|
||||
|
||||
/// <summary>是否选中</summary>
|
||||
public bool IsSelected
|
||||
{
|
||||
get { return _isSelected; }
|
||||
set
|
||||
{
|
||||
if (_isSelected == value) return;
|
||||
_isSelected = value;
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsSelected)));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>是否高亮(键盘上下选择时)</summary>
|
||||
public bool IsHighlighted
|
||||
{
|
||||
get { return _isHighlighted; }
|
||||
set
|
||||
{
|
||||
if (_isHighlighted == value) return;
|
||||
_isHighlighted = value;
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsHighlighted)));
|
||||
}
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 商铺筛选下拉 - 分组视图模型(分组头:展开/收起、全选复选框、数量)。
|
||||
/// 由 <see cref="ShopFilterComboBox"/> 内部维护,不直接使用。
|
||||
/// </summary>
|
||||
public class ShopFilterGroup : INotifyPropertyChanged
|
||||
{
|
||||
private bool _isExpanded;
|
||||
private bool? _isChecked;
|
||||
private string _countSuffix = "家";
|
||||
|
||||
public ShopFilterGroup()
|
||||
{
|
||||
_isExpanded = true;
|
||||
}
|
||||
|
||||
/// <summary>分组名称;空字符串表示未分组(隐藏分组头)</summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>分组内全部原始项(未过滤,用于计数与全选/清空)</summary>
|
||||
public List<ShopFilterItem> AllItems { get; } = new List<ShopFilterItem>();
|
||||
|
||||
/// <summary>分组内当前显示的项(受搜索过滤影响)</summary>
|
||||
public ObservableCollection<ShopFilterItem> Items { get; } = new ObservableCollection<ShopFilterItem>();
|
||||
|
||||
/// <summary>是否展开</summary>
|
||||
public bool IsExpanded
|
||||
{
|
||||
get { return _isExpanded; }
|
||||
set
|
||||
{
|
||||
if (_isExpanded == value) return;
|
||||
_isExpanded = value;
|
||||
Raise(nameof(IsExpanded));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>分组头复选框状态:true=全选,false=全不选,null=部分选中</summary>
|
||||
public bool? IsChecked
|
||||
{
|
||||
get { return _isChecked; }
|
||||
set
|
||||
{
|
||||
if (_isChecked == value) return;
|
||||
_isChecked = value;
|
||||
Raise(nameof(IsChecked));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>数量单位(如“家”)</summary>
|
||||
public string CountSuffix
|
||||
{
|
||||
get { return _countSuffix; }
|
||||
set
|
||||
{
|
||||
if (_countSuffix == value) return;
|
||||
_countSuffix = value;
|
||||
Raise(nameof(CountText));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>是否显示分组头(未分组时隐藏)</summary>
|
||||
public bool IsHeaderVisible
|
||||
{
|
||||
get { return !string.IsNullOrEmpty(Name); }
|
||||
}
|
||||
|
||||
/// <summary>分组数量文本(如“12家”)</summary>
|
||||
public string CountText
|
||||
{
|
||||
get { return Items.Count + CountSuffix; }
|
||||
}
|
||||
|
||||
/// <summary>过滤后刷新数量文本</summary>
|
||||
public void RefreshCountText()
|
||||
{
|
||||
Raise(nameof(CountText));
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
private void Raise(string propertyName)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,8 @@ namespace _29WxOrderWpf.Ctrls.Statistics.Models
|
||||
/// <summary>结束时间</summary>
|
||||
public DateTime EndDate { get; set; }
|
||||
|
||||
/// <summary>店铺 shop_id(B.shop_id),空/Null 表示全部店铺</summary>
|
||||
public string ShopKey { get; set; }
|
||||
/// <summary>选中的店铺 app_key 列表;空/Null 表示全部店铺</summary>
|
||||
public List<string> ShopKeys { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
x:Class="_29WxOrderWpf.Ctrls.Statistics.RevenueStatisticsSearchBar"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:c="clr-namespace:_29WxOrderWpf.Ctrls.Commons"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="clr-namespace:_29WxOrderWpf.Ctrls.Statistics.ViewModels"
|
||||
d:DataContext="{d:DesignInstance Type=vm:ShopRevenueStatisticsViewModel,IsDesignTimeCreatable=True}"
|
||||
d:DataContext="{d:DesignInstance Type=vm:ShopRevenueStatisticsViewModel,
|
||||
IsDesignTimeCreatable=True}"
|
||||
mc:Ignorable="d">
|
||||
<UserControl.Resources>
|
||||
<!-- 日期只允许从日历选择,禁止手工输入 -->
|
||||
@@ -42,21 +44,23 @@
|
||||
FontSize="14"
|
||||
Foreground="#666666"
|
||||
Text="时间" />
|
||||
<ComboBox IsEnabled="False"
|
||||
<ComboBox
|
||||
Width="110"
|
||||
Height="32"
|
||||
VerticalContentAlignment="Center"
|
||||
DisplayMemberPath="Text"
|
||||
IsReadOnly="True"
|
||||
BorderThickness="1"
|
||||
ItemsSource="{Binding TimeRangeOptions}"
|
||||
SelectedItem="{Binding SelectedTimeRange, Mode=TwoWay}"
|
||||
Style="{StaticResource BaseComboBoxStyle}" />
|
||||
Style="{StaticResource RoundedComboBox}" />
|
||||
<TextBlock
|
||||
Margin="14,0,6,0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Foreground="#666666"
|
||||
Text="日期" />
|
||||
<DatePicker
|
||||
<DatePicker BorderBrush="{StaticResource pageColorBrush}"
|
||||
Height="32"
|
||||
VerticalContentAlignment="Center"
|
||||
SelectedDate="{Binding StartDate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
@@ -66,7 +70,7 @@
|
||||
FontSize="14"
|
||||
Foreground="#666666"
|
||||
Text="~" />
|
||||
<DatePicker
|
||||
<DatePicker BorderBrush="{StaticResource pageColorBrush}"
|
||||
Height="32"
|
||||
VerticalContentAlignment="Center"
|
||||
SelectedDate="{Binding EndDate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
@@ -76,14 +80,21 @@
|
||||
FontSize="14"
|
||||
Foreground="#666666"
|
||||
Text="店铺" />
|
||||
<ComboBox IsEnabled="False"
|
||||
<c:ShopFilterComboBox
|
||||
Width="130"
|
||||
Height="32"
|
||||
DisplayMemberPath="ShopName"
|
||||
IsGrouped="True"
|
||||
ItemsSource="{Binding ShopOptions}"
|
||||
SelectedItems="{Binding SelectedShops}" />
|
||||
<!--<ComboBox IsEnabled="False"
|
||||
Width="130"
|
||||
Height="32"
|
||||
VerticalContentAlignment="Center"
|
||||
DisplayMemberPath="ShopName"
|
||||
ItemsSource="{Binding ShopOptions}"
|
||||
SelectedItem="{Binding SelectedShop, Mode=TwoWay}"
|
||||
Style="{StaticResource BaseComboBoxStyle}" />
|
||||
Style="{StaticResource BaseComboBoxStyle}" />-->
|
||||
<Button
|
||||
Height="32"
|
||||
Margin="18,0,0,0"
|
||||
|
||||
@@ -431,8 +431,8 @@ namespace _29WxOrderWpf.Ctrls.Statistics.Services
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 按天从缓存拼装完整范围,再按所选店铺过滤(ShopKey 为空 = 全部店铺)
|
||||
return FilterByShop(GetCachedRows(startDay, endDay), query.ShopKey);
|
||||
// 3. 按天从缓存拼装完整范围,再按所选店铺过滤(ShopKeys 为空 = 全部店铺)
|
||||
return FilterByShop(GetCachedRows(startDay, endDay), query.ShopKeys);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -477,21 +477,24 @@ namespace _29WxOrderWpf.Ctrls.Statistics.Services
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>按所选店铺过滤明细:ShopKey 为空返回全部;否则按店铺名(ShopName)精确匹配。</summary>
|
||||
private static List<ShopRevenueStatisticsRow> FilterByShop(List<ShopRevenueStatisticsRow> rows, string shopKey)
|
||||
/// <summary>按所选店铺过滤明细:ShopKeys 为空/Null 返回全部;否则按店铺名(ShopName)精确匹配。</summary>
|
||||
private static List<ShopRevenueStatisticsRow> FilterByShop(List<ShopRevenueStatisticsRow> rows, List<string> shopKeys)
|
||||
{
|
||||
if (rows == null)
|
||||
return new List<ShopRevenueStatisticsRow>();
|
||||
|
||||
if (string.IsNullOrEmpty(shopKey))
|
||||
if (shopKeys == null || shopKeys.Count == 0)
|
||||
return new List<ShopRevenueStatisticsRow>(rows);
|
||||
|
||||
var shop = GetShopOptions().FirstOrDefault(a => a.AppKey == shopKey);
|
||||
if (shop == null || string.IsNullOrEmpty(shop.ShopName))
|
||||
var shops = GetShopOptions()
|
||||
.Where(a => shopKeys.Contains(a.AppKey))
|
||||
.Where(a => !string.IsNullOrEmpty(a.ShopName))
|
||||
.ToList();
|
||||
if (shops.Count == 0)
|
||||
return new List<ShopRevenueStatisticsRow>();
|
||||
|
||||
return rows
|
||||
.Where(r => string.Equals(r.ShopName, shop.ShopName, StringComparison.OrdinalIgnoreCase))
|
||||
.Where(r => shops.Any(s => string.Equals(r.ShopName, s.ShopName, StringComparison.OrdinalIgnoreCase)))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace _29WxOrderWpf.Ctrls.Statistics.ViewModels
|
||||
public class PeriodRevenueStatisticsViewModel : PropertyNotifyObject
|
||||
{
|
||||
/// <summary>每页行数</summary>
|
||||
private const int PageSize = 20;
|
||||
private const int PageSize = 50;
|
||||
|
||||
private readonly IShopRevenueStatisticsService _service;
|
||||
private readonly PeriodStatisticType _periodType;
|
||||
@@ -29,7 +29,7 @@ namespace _29WxOrderWpf.Ctrls.Statistics.ViewModels
|
||||
private readonly DelegateCommand _nextPageCommand;
|
||||
private readonly DelegateCommand _lastPageCommand;
|
||||
|
||||
private ShopOption _selectedShop;
|
||||
private ObservableCollection<ShopOption> _selectedShops;
|
||||
private ShopRevenueSummary _summary;
|
||||
private ObservableCollection<ShopPeriodStatisticsItem> _statistics;
|
||||
private List<ShopPeriodStatisticsItem> _allItems = new List<ShopPeriodStatisticsItem>();
|
||||
@@ -192,17 +192,17 @@ namespace _29WxOrderWpf.Ctrls.Statistics.ViewModels
|
||||
set { this.SetValue(x => x.EndDate, value); }
|
||||
}
|
||||
|
||||
/// <summary>店铺选项(含“全部店铺”)</summary>
|
||||
/// <summary>店铺选项(“全部店铺”由多选控件“全部商铺”表示)</summary>
|
||||
public ObservableCollection<ShopOption> ShopOptions { get; }
|
||||
|
||||
/// <summary>选中的店铺</summary>
|
||||
public ShopOption SelectedShop
|
||||
/// <summary>选中的店铺(多选,绑定 ShopFilterComboBox.SelectedItems)</summary>
|
||||
public ObservableCollection<ShopOption> SelectedShops
|
||||
{
|
||||
get { return _selectedShop; }
|
||||
get { return _selectedShops; }
|
||||
set
|
||||
{
|
||||
_selectedShop = value;
|
||||
OnPropertyChanged(nameof(SelectedShop));
|
||||
_selectedShops = value;
|
||||
OnPropertyChanged(nameof(SelectedShops));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -378,12 +378,11 @@ namespace _29WxOrderWpf.Ctrls.Statistics.ViewModels
|
||||
{
|
||||
try
|
||||
{
|
||||
// 店铺选项
|
||||
ShopOptions.Add(new ShopOption { AppKey = string.Empty, ShopName = "全部店铺" });
|
||||
// 店铺选项(全部店铺由多选控件“全部商铺”表示,不再额外加入“全部店铺”项)
|
||||
var shops = await _service.GetShopOptionsAsync();
|
||||
foreach (var shop in shops)
|
||||
ShopOptions.Add(shop);
|
||||
SelectedShop = ShopOptions.FirstOrDefault();
|
||||
SelectedShops = new ObservableCollection<ShopOption>(ShopOptions);
|
||||
|
||||
// 默认时间范围
|
||||
SelectedTimeRange = TimeRangeOptions.FirstOrDefault(t => t.Value == GetDefaultTimeRangeValue(_periodType));
|
||||
@@ -477,6 +476,23 @@ namespace _29WxOrderWpf.Ctrls.Statistics.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>根据选中店铺生成查询店铺 key 列表:未选/全选/选中“全部店铺”时返回空列表(表示全部店铺)</summary>
|
||||
private List<string> BuildSelectedShopKeys()
|
||||
{
|
||||
var selected = SelectedShops;
|
||||
if (selected == null || selected.Count == 0)
|
||||
return new List<string>();
|
||||
|
||||
var keys = selected.Select(s => s == null ? string.Empty : s.AppKey).ToList();
|
||||
if (keys.Any(string.IsNullOrEmpty))
|
||||
return new List<string>();
|
||||
|
||||
if (selected.Count >= ShopOptions.Count)
|
||||
return new List<string>();
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
/// <summary>执行查询,刷新周期统计表格并回到第一页</summary>
|
||||
private async Task LoadAsync()
|
||||
{
|
||||
@@ -494,7 +510,7 @@ namespace _29WxOrderWpf.Ctrls.Statistics.ViewModels
|
||||
{
|
||||
StartDate = StartDate,
|
||||
EndDate = EndDate,
|
||||
ShopKey = SelectedShop == null ? string.Empty : SelectedShop.AppKey,
|
||||
ShopKeys = BuildSelectedShopKeys(),
|
||||
};
|
||||
|
||||
var table = await _service.GetPeriodStatisticsAsync(query, _periodType);
|
||||
@@ -518,7 +534,7 @@ namespace _29WxOrderWpf.Ctrls.Statistics.ViewModels
|
||||
/// <summary>重置查询条件并重新加载</summary>
|
||||
private async void Reset()
|
||||
{
|
||||
SelectedShop = ShopOptions.FirstOrDefault();
|
||||
SelectedShops = new ObservableCollection<ShopOption>(ShopOptions);
|
||||
SelectedTimeRange = TimeRangeOptions.FirstOrDefault(t => t.Value == GetDefaultTimeRangeValue(_periodType));
|
||||
ApplyTimeRange();
|
||||
await LoadAsync();
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace _29WxOrderWpf.Ctrls.Statistics.ViewModels
|
||||
private ObservableCollection<RevenueTrendPoint> _trendData;
|
||||
private ObservableCollection<ShopSalesProportionItem> _shopProportionData;
|
||||
private ObservableCollection<ShopStatisticsItem> _shopStatistics;
|
||||
private ShopOption _selectedShop;
|
||||
private ObservableCollection<ShopOption> _selectedShops;
|
||||
private string _selectedPlatform;
|
||||
|
||||
public ShopRevenueStatisticsViewModel()
|
||||
@@ -92,17 +92,17 @@ namespace _29WxOrderWpf.Ctrls.Statistics.ViewModels
|
||||
set { this.SetValue(x => x.EndDate, value); }
|
||||
}
|
||||
|
||||
/// <summary>店铺选项(含“全部店铺”)</summary>
|
||||
/// <summary>店铺选项(“全部店铺”由多选控件“全部商铺”表示)</summary>
|
||||
public ObservableCollection<ShopOption> ShopOptions { get; }
|
||||
|
||||
/// <summary>选中的店铺</summary>
|
||||
public ShopOption SelectedShop
|
||||
public ObservableCollection<ShopOption> SelectedShops
|
||||
{
|
||||
get { return _selectedShop; }
|
||||
get { return _selectedShops; }
|
||||
set
|
||||
{
|
||||
_selectedShop = value;
|
||||
OnPropertyChanged(nameof(SelectedShop));
|
||||
_selectedShops = value;
|
||||
OnPropertyChanged(nameof(SelectedShops));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,12 +179,11 @@ namespace _29WxOrderWpf.Ctrls.Statistics.ViewModels
|
||||
{
|
||||
try
|
||||
{
|
||||
// 店铺选项
|
||||
ShopOptions.Add(new ShopOption { AppKey = string.Empty, ShopName = "全部店铺" });
|
||||
// 店铺选项(全部店铺由多选控件“全部商铺”表示,不再额外加入“全部店铺”项)
|
||||
var shops = await _service.GetShopOptionsAsync();
|
||||
foreach (var shop in shops)
|
||||
ShopOptions.Add(shop);
|
||||
SelectedShop = ShopOptions.FirstOrDefault();
|
||||
SelectedShops = new ObservableCollection<ShopOption>(ShopOptions);
|
||||
|
||||
// 默认时间范围:本月
|
||||
SelectedTimeRange = TimeRangeOptions.FirstOrDefault(t => t.Value == "month");
|
||||
@@ -233,6 +232,23 @@ namespace _29WxOrderWpf.Ctrls.Statistics.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>根据选中店铺生成查询店铺 key 列表:未选/全选/选中“全部店铺”时返回空列表(表示全部店铺)</summary>
|
||||
private List<string> BuildSelectedShopKeys()
|
||||
{
|
||||
var selected = SelectedShops;
|
||||
if (selected == null || selected.Count == 0)
|
||||
return new List<string>();
|
||||
|
||||
var keys = selected.Select(s => s == null ? string.Empty : s.AppKey).ToList();
|
||||
if (keys.Any(string.IsNullOrEmpty))
|
||||
return new List<string>();
|
||||
|
||||
if (selected.Count >= ShopOptions.Count)
|
||||
return new List<string>();
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
/// <summary>执行查询,刷新页面所有统计数据</summary>
|
||||
private async Task LoadAsync()
|
||||
{
|
||||
@@ -250,7 +266,7 @@ namespace _29WxOrderWpf.Ctrls.Statistics.ViewModels
|
||||
{
|
||||
StartDate = StartDate,
|
||||
EndDate = EndDate,
|
||||
ShopKey = SelectedShop == null ? string.Empty : SelectedShop.AppKey,
|
||||
ShopKeys = BuildSelectedShopKeys(),
|
||||
};
|
||||
|
||||
Summary = await _service.GetSummaryAsync(query);
|
||||
@@ -277,7 +293,7 @@ namespace _29WxOrderWpf.Ctrls.Statistics.ViewModels
|
||||
/// <summary>重置查询条件并重新加载</summary>
|
||||
private async void Reset()
|
||||
{
|
||||
SelectedShop = ShopOptions.FirstOrDefault();
|
||||
SelectedShops = new ObservableCollection<ShopOption>(ShopOptions);
|
||||
SelectedTimeRange = TimeRangeOptions.FirstOrDefault(t => t.Value == "month");
|
||||
ApplyTimeRange();
|
||||
await LoadAsync();
|
||||
|
||||
Reference in new issue
Block a user