【问题标题】:TypeScript enum to object arrayTypeScript 枚举到对象数组
【发布时间】:2017-08-23 08:47:50
【问题描述】:

我有一个这样定义的枚举:

export enum GoalProgressMeasurements {
    Percentage = 1,
    Numeric_Target = 2,
    Completed_Tasks = 3,
    Average_Milestone_Progress = 4,
    Not_Measured = 5
}

但是,我希望将它表示为来自我们 API 的对象数组/列表,如下所示:

[{id: 1, name: 'Percentage'}, 
 {id: 2, name: 'Numeric Target'},
 {id: 3, name: 'Completed Tasks'},
 {id: 4, name: 'Average Milestone Progress'},
 {id: 5, name: 'Not Measured'}]

是否有简单且原生的方法来做到这一点,还是我必须构建一个将枚举转换为 int 和字符串的函数,并将对象构建到数组中?

【问题讨论】:

  • 枚举是运行时存在的真实对象。因此,您可以通过以下方式反转映射:GoalProgressMeasurements[GoalProgressMeasurements.Completed_Tasks] 以获取枚举名称。我不知道这是否有帮助。
  • 你能否给“来自我们的API”一个更好的描述,或者给出一个使用例子

标签: javascript arrays typescript enums casting


【解决方案1】:

枚举是运行时存在的真实对象。因此,您可以通过以下方式反转映射:

let value = GoalProgressMeasurements.Not_Measured;
console.log(GoalProgressMeasurements[value]);
// => Not_Measured

基于此,您可以使用以下代码:

export enum GoalProgressMeasurements {
    Percentage = 1,
    Numeric_Target = 2,
    Completed_Tasks = 3,
    Average_Milestone_Progress = 4,
    Not_Measured = 5
}

let map: {id: number; name: string}[] = [];

for(var n in GoalProgressMeasurements) {
    if (typeof GoalProgressMeasurements[n] === 'number') {
        map.push({id: <any>GoalProgressMeasurements[n], name: n});
    }
}

console.log(map);

参考:https://www.typescriptlang.org/docs/handbook/enums.html

【讨论】:

  • = 5 之前,您不需要写入默认值= 2 - = 1 之后的任何内容都会自动+1。
  • 也许你不需要,但它更具表现力。恕我直言,这使它变得更好。
  • 请注意,这不适用于字符串值枚举
【解决方案2】:

简单的解决方案。您可以使用以下函数将 Enum 转换为对象数组。

 buildGoalProgressMeasurementsArray(): Object[] {

    return Object.keys(GoalProgressMeasurements)
              .map(key => ({ id: GoalProgressMeasurements[key], name: key }))
 }

如果您需要去掉该下划线,我们可以使用正则表达式,如下所示:

buildGoalProgressMeasurementsArray(): Object[] {

    return Object.keys(GoalProgressMeasurements)
              .map(key => ({ id: GoalProgressMeasurements[key], name: key.replace(/_/g, ' ') }))
 }

【讨论】:

  • 你应该过滤类型号为Object.keys(GoalProgressMeasurements) .filter(key =&gt; typeof GoalProgressMeasurements[key] === 'number') .map(key =&gt; ({ id: GoalProgressMeasurements[key], name: key }))的键
  • 非常适合基于字符串的枚举,例如:export enum UserRole { STUDENT = 'Estudiante', DIRECTOR = 'Director de carrera', AUTHORITY = 'Autoridad', FINANCIAL = 'Dirección Financiera' }
【解决方案3】:

一个棘手的问题是 TypeScript 会将枚举“加倍”映射到发出的对象中,因此可以通过键和值访问它。

enum MyEnum {
    Part1 = 0,
    Part2 = 1
}

将被发射为

{
   Part1: 0,
   Part2: 1,
   0: 'Part1',
   1: 'Part2'
}

所以你应该在映射之前先过滤对象。所以@Diullei 的解决方案有正确的答案。这是我的实现:

// Helper
const StringIsNumber = value => isNaN(Number(value)) === false;

// Turn enum into array
function ToArray(enumme) {
    return Object.keys(enumme)
        .filter(StringIsNumber)
        .map(key => enumme[key]);
}

像这样使用它:

export enum GoalProgressMeasurements {
    Percentage,
    Numeric_Target,
    Completed_Tasks,
    Average_Milestone_Progress,
    Not_Measured
}

console.log(ToArray(GoalProgressMeasurements));

【讨论】:

  • 嗯,如果 enum MyEnum { Part1 = 0, Part2 = 1 } 变成 { Part1: 0, Part2: 1, 0: 'Part1', 1: 'Part2' } 那么,为什么当你 console.log(Object.values(MyEnum)) 它只打印 0,1?
  • @JuanJoséRamírez 你在哪里看到的?对我来说,Object.values(MyEnum) 等于 ["Part1", "Part2", 0, 1]
  • 我刚刚在我的组件中打印了console.log(Object.values(MyEnum))。我正在使用角度,不确定这是否相关。我对 TypeScript 没有那么丰富的经验
  • 行为会随着不同的TS版本而改变吗?
  • 我一直在检查文档typescriptlang.org/docs/handbook/release-notes/…,似乎字符串枚举有不同的行为。他们根本没有生成反向映射。在我的代码中,我使用的是字符串枚举,而不是本示例中的字符串。
【解决方案4】:

你可以这样做:

export enum GoalProgressMeasurements {
    Percentage = 1,
    Numeric_Target = 2,
    Completed_Tasks = 3,
    Average_Milestone_Progress = 4,
    Not_Measured = 5
}

export class GoalProgressMeasurement {
    constructor(public goalProgressMeasurement: GoalProgressMeasurements, public name: string) {
    }
}

export var goalProgressMeasurements: { [key: number]: GoalProgressMeasurement } = {
    1: new GoalProgressMeasurement(GoalProgressMeasurements.Percentage, "Percentage"),
    2: new GoalProgressMeasurement(GoalProgressMeasurements.Numeric_Target, "Numeric Target"),
    3: new GoalProgressMeasurement(GoalProgressMeasurements.Completed_Tasks, "Completed Tasks"),
    4: new GoalProgressMeasurement(GoalProgressMeasurements.Average_Milestone_Progress, "Average Milestone Progress"),
    5: new GoalProgressMeasurement(GoalProgressMeasurements.Not_Measured, "Not Measured"),
}

你可以这样使用它:

var gpm: GoalProgressMeasurement = goalProgressMeasurements[GoalProgressMeasurements.Percentage];
var gpmName: string = gpm.name;

var myProgressId: number = 1; // the value can come out of drop down selected value or from back-end , so you can imagine the way of using
var gpm2: GoalProgressMeasurement = goalProgressMeasurements[myProgressId];
var gpmName: string = gpm.name;

您可以根据需要使用对象的其他属性扩展 GoalProgressMeasurement。我将这种方法用于每个应该是包含更多值的对象的枚举。

【讨论】:

    【解决方案5】:
    class EnumHelpers {
    
        static getNamesAndValues<T extends number>(e: any) {
            return EnumHelpers.getNames(e).map(n => ({ name: n, value: e[n] as T }));
        }
    
        static getNames(e: any) {
            return EnumHelpers.getObjValues(e).filter(v => typeof v === 'string') as string[];
        }
    
        static getValues<T extends number>(e: any) {
            return EnumHelpers.getObjValues(e).filter(v => typeof v === 'number') as T[];
        }
    
        static getSelectList<T extends number, U>(e: any, stringConverter: (arg: U) => string) {
            const selectList = new Map<T, string>();
            this.getValues(e).forEach(val => selectList.set(val as T, stringConverter(val as unknown as U)));
            return selectList;
        }
    
        static getSelectListAsArray<T extends number, U>(e: any, stringConverter: (arg: U) => string) {
            return Array.from(this.getSelectList(e, stringConverter), value => ({ value: value[0] as T, presentation: value[1] }));
        }
    
        private static getObjValues(e: any): (number | string)[] {
            return Object.keys(e).map(k => e[k]);
        }
    }
    

    【讨论】:

    • 感谢这些帮助。非常有用。
    • 谢谢。节省了我几个小时的乱七八糟的时间。真正有用的可重复使用的
    【解决方案6】:

    如果您使用的是 ES8

    只有在这种情况下,它才能完全正常工作。它将为您提供给定 enum 的值数组。

    enum Colors {
      WHITE = 0,
      BLACK = 1,
      BLUE = 3
    }
    
    const colorValueArray = Object.values(Colors); //[ 'WHITE', 'BLACK', 'BLUE', 0, 1, 3 ]
    

    你会得到colorValueArray 这样的[ 'WHITE', 'BLACK', 'BLUE', 0, 1, 3 ]。所有的键都在数组的前半部分,所有的值都在后半部分。

    即使是这种枚举也可以正常工作

    enum Operation {
        READ,
        WRITE,
        EXECUTE
    }
    

    但是这个解决方案不适用于像这样的异构枚举

    enum BooleanLikeHeterogeneousEnum {
      No = 0,
      Yes = "YES",
    }
    

    【讨论】:

    • 请记住,这会产生重复。每个元素的字符串值和数值,即 (string | YourEnumType)[] 类型,这不是您在每种情况下可能想要的。
    • 是否保证前半部分是键,后半部分是值?有参考吗?
    • Object.values() 不是 ES6 的一部分。它是 ES2017 的一部分。
    • 注意没有 ES8 这样的东西;正如@atiyar 所说,它被称为 ES2017。
    【解决方案7】:

    enum GoalProgressMeasurements {
        Percentage = 1,
        Numeric_Target = 2,
        Completed_Tasks = 3,
        Average_Milestone_Progress = 4,
        Not_Measured = 5
    }
        
    const array = []
        
    for (const [key, value] of Object.entries(GoalProgressMeasurements)) {
        if (!Number.isNaN(Number(key))) {
            continue;
        }
    
        array.push({ id: value, name: key.replace('_', '') });
    }
    
    console.log(array);

    【讨论】:

    • 请始终将您的答案放在上下文中,而不仅仅是粘贴代码。有关详细信息,请参阅here
    【解决方案8】:

    我不喜欢上述任何答案,因为它们都不能正确处理可以是 TypeScript 枚举值的字符串/数字的混合。

    以下函数遵循 TypeScript 枚举的语义,以提供正确的键到值映射。从那里,获取对象数组或仅键或仅值是微不足道的。

    /**
     * Converts the given enum to a map of the keys to the values.
     * @param enumeration The enum to convert to a map.
     */
    function enumToMap(enumeration: any): Map<string, string | number> {
      const map = new Map<string, string | number>();
      for (let key in enumeration) {
          //TypeScript does not allow enum keys to be numeric
          if (!isNaN(Number(key))) continue;
    
          const val = enumeration[key] as string | number;
    
          //TypeScript does not allow enum value to be null or undefined
          if (val !== undefined && val !== null)
              map.set(key, val);
      }
    
      return map;
    }
    

    示例用法:

    enum Dog {
        Rover = 1,
        Lassie = "Collie",
        Fido = 3,
        Cody = "Mutt",
    }
    
    let map = enumToMap(Dog); //Map of keys to values
    
    let objs = Array.from(map.entries()).map(m => ({id: m[1], name: m[0]})); //Objects as asked for in OP
    let entries = Array.from(map.entries()); //Array of each entry
    let keys = Array.from(map.keys()); //An array of keys
    let values = Array.from(map.values()); //An array of values
    

    我还要指出,OP 正在向后考虑枚举。枚举中的“键”在技术上位于左侧,值位于右侧。 TypeScript 允许您尽可能多地重复 RHS 上的值。

    【讨论】:

      【解决方案9】:

      首先我们得到这个枚举的一个键数组。然后,使用 map() 函数,我们将数据转换为所需的格式。 id是从key中获取的,name是通过同一个key从enum中获取的。

      const converted = Object.keys(GoalProgressMeasurements).map(key => {
              return {
                  id: GoalProgressMeasurements[key],
                  name: key,
              };
          });
      

      【讨论】:

      • 欢迎来到stackoverflow。在回答问题时,最好解释一下您的代码 sn-p 的作用。有关详细信息,请参阅此处:How to Answer
      • 请考虑在您的答案中添加一些解释或细节。虽然它可能会回答问题,但只是添加一段代码作为答案,并不能帮助 OP 或未来的社区成员理解问题或建议的解决方案。
      【解决方案10】:

      我用

      Object.entries(GoalProgressMeasurement).filter(e => !isNaN(e[0]as any)).map(e => ({ name: e[1], id: e[0] }));
      

      简单的 1 行就可以完成这项工作。

      只需 3 个简单的步骤即可完成工作
      - 使用Object.entries 加载键和值的组合。
      - 过滤掉非数字(因为打字稿生成反向查找的值)。
      - 然后我们把它映射到我们喜欢的数组对象上。

      【讨论】:

      • 与前端的 IE 不兼容(不应该支持 ie 是一个很好的答案......但我猜是客户)。希望 babel 能够实现它,但由于我尚未验证它,所以坚持使用其他方法
      • 字符串枚举很简单,只要Object.values(GoalProgressMeasurement)
      【解决方案11】:

      这将返回一个枚举值数组:

       Object.values(myEnum);
      

      【讨论】:

      • 因为它没有给出正确的结果,请检查stackoverflow.com/a/57266281/3548345
      • @walox,它只给出正确的结果,您给出的链接没有显示正确的值。理想情况下,object.keys 将返回键数组,object.values 将返回值数组。
      【解决方案12】:

      由于具有字符串值的枚举与具有数字值的枚举不同,因此最好从@user8363 解决方案中过滤非数字。

      以下是如何从枚举字符串、混合数中获取值:

          //Helper
          export const StringIsNotNumber = value => isNaN(Number(value)) === true;
          
          // Turn enum into array
          export function enumToArray(enumme) {
            return Object.keys(enumme)
             .filter(StringIsNotNumber)
             .map(key => enumme[key]);
          }

      【讨论】:

        【解决方案13】:

        我很惊讶在 TypeScript 线程中没有人提供有效的 TypeScript 函数并支持打字。这是@user8363 解决方案的变体:

        const isStringNumber = (value: string) => isNaN(Number(value)) === false;
        
        function enumToArray<T extends {}>(givenEnum: T) {
          return (Object.keys(givenEnum).filter(isStringNumber) as (keyof T)[]).map(
            (key) => givenEnum[key]
          );
        }
        

        【讨论】:

          【解决方案14】:

          有一个简单的解决方案,所以当你运行Object.keys(Enum) 时,它会给你一个值和键数组,在第一个切片值和第二个键中,所以为什么我们不只返回第二个切片,下面的这段代码对我有用。

          enum Enum {
             ONE,
             TWO,
             THREE,
             FOUR,
             FIVE,
             SIX,
             SEVEN
          }
          const keys = Object.keys(Enum); 
          console.log(keys.slice(keys.length / 2));
          

          【讨论】:

          • 很好奇为什么这被否决了?我也有同样的想法。是否会担心 TypeScript 会改变并且不再以相同的方式制作枚举?
          • 不保证Object.keys 的顺序。
          【解决方案15】:

          我认为不能保证顺序,否则很容易将Object.entries结果的后半部分切片并从那里映射。

          上述答案的唯一(非常小的)问题是

          • 字符串和数字之间有很多不必要的类型转换。
          • 当一次迭代同样干净有效时,条目将被迭代两次。
          type StandardEnum = { [id: string]: number | string; [nu: number]: string;}
          
          function enumToList<T extends StandardEnum> (enm: T) : { id: number; description: string }[] {
              return Object.entries(enm).reduce((accum, kv) => {
                  if (typeof kv[1] === 'number') {
                      accum.push({ id: kv[1], description: kv[0] })
                  }
                  return accum
              }, []) // if enum is huge, perhaps pre-allocate with new Array(entries.length / 2), however then push won't work, so tracking an index would also be required
          }
          

          【讨论】:

            【解决方案16】:

            感谢polkovnikov.ph,我终于找到了适用于大多数用例的解决方案。

            问题的有效解决方案

            type Descripted<T> = {
                [K in keyof T]: {
                    readonly id: T[K];
                    readonly description: string;
                }
            }[keyof T]
            
            /**
             * Helper to produce an array of enum descriptors.
             * @param enumeration Enumeration object.
             * @param separatorRegex Regex that would catch the separator in your enum key.
             */
            export function enumToDescriptedArray<T>(enumeration: T, separatorRegex: RegExp = /_/g): Descripted<T>[] {
                return (Object.keys(enumeration) as Array<keyof T>)
                    .filter(key => isNaN(Number(key)))
                    .filter(key => typeof enumeration[key] === "number" || typeof enumeration[key] === "string")
                    .map(key => ({
                        id: enumeration[key],
                        description: String(key).replace(separatorRegex, ' '),
                    }));
            }
            

            例子:

            
            export enum GoalProgressMeasurements {
                Percentage = 1,
                Numeric_Target = 2,
                Completed_Tasks = 3,
                Average_Milestone_Progress = 4,
                Not_Measured = 5
            }
            
            console.log(enumToDescriptedArray(GoalProgressMeasurements))
            // Produces:
            /*
            [
                {id: 1, description: "Percentage"},
                {id: 2, description: "Numeric Target"},
                {id: 3, description: "Completed Tasks"},
                {id: 4, description: "Average Milestone Progress"},
                {id: 5, description: "Not Measured"}
            ]
            */
            

            另外,我使用了一个有用的 util 函数来将枚举对象映射到它具有的可用值数组:

            映射器

            type NonFunctional<T> = T extends Function ? never : T;
            
            /**
             * Helper to produce an array of enum values.
             * @param enumeration Enumeration object.
             */
            export function enumToArray<T>(enumeration: T): NonFunctional<T[keyof T]>[] {
                return Object.keys(enumeration)
                    .filter(key => isNaN(Number(key)))
                    .map(key => enumeration[key])
                    .filter(val => typeof val === "number" || typeof val === "string");
            }
            

            工作用例

            • 数字枚举
            enum Colors1 {
                WHITE = 0,
                BLACK = 1
            }
            console.log(Object.values(Colors1)); // ['WHITE', 'BLACK', 0, 1]
            console.log(enumToArray(Colors1));   // [0, 1]
            
            • 字符串枚举
            enum Colors2 {
                WHITE = "white",
                BLACK = "black"
            }
            console.log(Object.values(Colors2)); // ['white', 'black']
            console.log(enumToArray(Colors2));   // ['white', 'black']
            
            • 异构枚举
            enum Colors4 {
                WHITE = "white",
                BLACK = 0
            }
            console.log(Object.values(Colors4)); // ["BLACK", "white", 0]
            console.log(enumToArray(Colors4));   // ["white", 0]
            
            • 枚举与具有导出函数的命名空间合并
            
            enum Colors3 {
                WHITE = "white",
                BLACK = "black"
            }
            namespace Colors3 {
                export function fun() {}
            }
            console.log(Object.values(Colors3)); // ['white', 'black', Function]
            console.log(enumToArray(Colors3));   // ['white', 'black']
            

            【讨论】:

            • Object.values 以与for..in 循环相同的顺序返回属性,for..in 以任意顺序返回它们。此代码可以返回任意一组键和值,具体取决于平台。
            • @polkovnikov.ph 你说得对,谢谢!现在新的实现不依赖Object.values的顺序了
            • 如果有数值,也可以是异构枚举(见文档),这个实现会丢失string值。而且它仍然没有回答原始问题。
            • 谢谢,我尝试改进答案并为问题添加了确切的解决方案。
            【解决方案17】:
            function enumKeys(_enum) {
              const entries = Object.entries(_enum).filter(e => !isNaN(Number(e[0])));
              if (!entries.length) {
                // enum has string values so we can use Object.keys
                return Object.keys(_enum);
              }
              return entries.map(e => e[1]);
            }
            

            【讨论】:

              【解决方案18】:

              获取数组内枚举值的示例:

              export enum DocumentationTypeEnum {
                GDPR = 'GDPR',
                HELP = 'HELP',
                OTHER = 'OTHER',
                FOOTER = 'FOOTER'
              }
              const keys = Object.keys(DocumentationTypeEnum);
              
              console.log(keys); // Output :  ["GDPR", "HELP", "OTHER", "FOOTER"]
              

              【讨论】:

                【解决方案19】:

                TS:

                仅适用于短(

                const keys = Object.keys(Enum).filter((el: string) => el.length > 1)
                console.log(keys)
                
                1. Object.keys() 将返回一个数组,其中包含 ['0', '1', '2', 'enumElement1', 'enumElement2', enumElement3']
                2. filter() 获取每个元素并检查其长度(因为字符串)并从结果数组中排除所有数字

                【讨论】:

                  【解决方案20】:

                  使用ES8 Object.entries的另一种方法

                  export enum Weeks {  
                      MONDAY = 1,  
                      TUESDAY= 2,  
                      WEDNESDAY = 3,  
                      THURSDAY = 4,  
                      FRIDAY = 5,  
                      SATURDAY=6,  
                      SUNDAY=7,  
                  }
                  
                  
                  function convertEnumToArray(){
                     const arrayObjects = []            
                       // Retrieve key and values using Object.entries() method. 
                       for (const [propertyKey, propertyValue] of Object.entries(Weeks)) { 
                  
                        // Ignore keys that are not numbers
                        if (!Number.isNaN(Number(propertyKey))) {  
                          continue;  
                        }  
                  
                        // Add keys and values to array
                        arrayObjects.push({ id: propertyValue, name: propertyKey });  
                      }        
                  
                    console.log(arrayObjects); 
                  }
                  

                  将产生以下内容:

                  [ 
                    { id: 1, name: 'MONDAY' },  
                    { id: 2, name: 'TUESDAY' },  
                    { id: 3, name: 'WEDNESDAY' },  
                    { id: 4, name: 'THURSDAY' },  
                    { id: 5, name: 'FRIDAY' },  
                    { id: 6, name: 'SATURDAY' },  
                    { id: 7, name: 'SUNDAY' } 
                  ] 
                  

                  无耻地从这个blog盗取

                  【讨论】:

                    【解决方案21】:

                    我才几个月就知道 typescript,下面的解决方案对我有用。希望它也可以帮助某人-

                    export enum ScheduleType {
                      Basic = <any>'B',
                      Consolidated = <any>'C',
                    }
                    
                    scheduleTypes = Object.keys(ScheduleType)
                    .filter((k, i) => i % 2)
                    .map((key: any) => {
                      return {
                        systemValue: key,
                        displayValue: ScheduleType[key],
                      };
                    });
                    

                    它给出了以下结果 - [{显示值:“基本”,系统值:“B”}, {displayValue: "Consolidated", systemValue: "C"}]

                    【讨论】:

                      【解决方案22】:

                      此方法基于声明: 枚举的键不能是数字

                      export const isNumeric = (num?: Value | null): num is number => {
                        if (num === undefined || num === null) {
                          return false;
                        } 
                        
                        const number = +num;
                      
                        if (number - number !== 0) {
                          // Discard Infinity and NaN
                          return false;
                        }
                      
                        if (number === num) {
                          return true;
                        }
                      
                        if (typeof num === 'string') {
                          return !(number === 0 && num.trim() === '');
                        }
                        return false;
                      };
                      
                      enum En  {
                        ewq1 = 1,
                        we2 = 'ss',
                        sad = 'sad',
                      }
                      
                      type TEnum = {
                          [id: string]: number | string;
                      }
                      
                      export const getEnumValues = <T extends TEnum>(enumerable: T) =>
                        Object.keys(enumerable)
                          .filter((x) => !isNumeric(x))
                          .map((key) => enumerable[key] as T[keyof T]) 
                      
                      console.log(getEnumValues(En)) // [1, "ss", "sad"] 
                      

                      【讨论】:

                        【解决方案23】:

                        另一种方法是

                        export const GoalNames = {
                            [GoalProgressMeasurements.Percentage] = 'Percentage',
                            [GoalProgressMeasurements.Numeric_Target] = 'Numeric Target',
                            [GoalProgressMeasurements.Completed_Tasks] = 'Completed Tasks',
                            [GoalProgressMeasurements.Average_Milestone_Progress] = 'Average Milestone Progress',
                            [GoalProgressMeasurements.Not_Measured] = 'Not Measured'
                        }
                        

                        您可以致电:

                        const name = GoalNames[goalEnumVal];
                        

                        【讨论】:

                          【解决方案24】:

                          我是这样解决的

                                  const listKeys = Object.keys(TripStatus); //TripStatus is enum type
                                  const numOfItem = listKeys.length/2;
                                  for(let i=0; i<numOfItem; i++){
                                    this.listStatus.push({
                                      id: listKeys[i],
                                      name: listKeys[numOfItem+i]
                                    })
                                  }
                          

                          【讨论】:

                            【解决方案25】:

                            只有一行:

                            Object.entries(GoalProgressMeasurements).map(([key, value]) => ({id: key, value: value}))
                            

                            【讨论】:

                              猜你喜欢
                              • 2020-04-04
                              • 1970-01-01
                              • 2020-03-14
                              • 1970-01-01
                              • 2020-08-16
                              • 2022-09-23
                              • 2021-11-14
                              • 1970-01-01
                              • 1970-01-01
                              相关资源
                              最近更新 更多