Callback<> and Bind()

本文深入介绍了Callback类及其与Bind函数的配合使用,包括如何进行参数绑定、类型安全的部分应用及不同场景下的内存管理策略。此外,还探讨了OnceCallback与RepeatingCallback的区别,并提供了大量示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

Callback<> and Bind()

Introduction

The templated base::Callback<> class is a generalized function object. Together with the base::Bind() function in base/bind.h, they provide a type-safe method for performing partial application of functions.

Partial application (or “currying”) is the process of binding a subset of a function's arguments to produce another function that takes fewer arguments. This can be used to pass around a unit of delayed execution, much like lexical closures are used in other languages. For example, it is used in Chromium code to schedule tasks on different MessageLoops.

A callback with no unbound input parameters (base::Callback<void()>) is called a base::Closure. Note that this is NOT the same as what other languages refer to as a closure -- it does not retain a reference to its enclosing environment.

OnceCallback<> And RepeatingCallback<>

base::OnceCallback<> and base::RepeatingCallback<> are next gen callback classes, which are under development.

base::OnceCallback<> is created by base::BindOnce(). This is a callback variant that is a move-only type and can be run only once. This moves out bound parameters from its internal storage to the bound function by default, so it‘s easier to use with movable types. This should be the preferred callback type: since the lifetime of the callback is clear, it’s simpler to reason about when a callback that is passed between threads is destroyed.

base::RepeatingCallback<> is created by base::BindRepeating(). This is a callback variant that is copyable that can be run multiple times. It uses internal ref-counting to make copies cheap. However, since ownership is shared, it is harder to reason about when the callback and the bound state are destroyed, especially when the callback is passed between threads.

The legacy base::Callback<> is currently aliased to base::RepeatingCallback<>. In new code, prefer base::OnceCallback<> where possible, and use base::RepeatingCallback<> otherwise. Once the migration is complete, the type alias will be removed and base::OnceCallback<> will be renamed to base::Callback<> to emphasize that it should be preferred.

base::RepeatingCallback<> is convertible to base::OnceCallback<> by the implicit conversion.

Memory Management And Passing

Pass base::Callback objects by value if ownership is transferred; otherwise, pass it by const-reference.

// |Foo| just refers to |cb| but doesn't store it nor consume it.
bool Foo(const base::OnceCallback<void(int)>& cb) { return cb.is_null(); } // |Bar| takes the ownership of |cb| and stores |cb| into |g_cb|. base::OnceCallback<void(int)> g_cb; void Bar(base::OnceCallback<void(int)> cb) { g_cb = std::move(cb); } // |Baz| takes the ownership of |cb| and consumes |cb| by Run(). void Baz(base::OnceCallback<void(int)> cb) { std::move(cb).Run(42); } // |Qux| takes the ownership of |cb| and transfers ownership to PostTask(), // which also takes the ownership of |cb|. void Qux(base::OnceCallback<void(int)> cb) { PostTask(FROM_HERE, base::BindOnce(std::move(cb), 42)); }

When you pass a base::Callback object to a function parameter, use std::move() if you don‘t need to keep a reference to it, otherwise, pass the object directly. You may see a compile error when the function requires the exclusive ownership, and you didn’t pass the callback by move. Note that the moved-from base::Callback becomes null, as if its Reset() method had been called, and its is_null() method will return true.

Quick reference for basic stuff

Binding A Bare Function

int Return5() { return 5; } base::OnceCallback<int()> func_cb = base::BindOnce(&Return5); LOG(INFO) << std::move(func_cb).Run(); // Prints 5.
int Return5() { return 5; } base::RepeatingCallback<int()> func_cb = base::BindRepeating(&Return5); LOG(INFO) << func_cb.Run(); // Prints 5.

Binding A Captureless Lambda

base::Callback<int()> lambda_cb = base::Bind([] { return 4; }); LOG(INFO) << lambda_cb.Run(); // Print 4. base::OnceCallback<int()> lambda_cb2 = base::BindOnce([] { return 3; }); LOG(INFO) << std::move(lambda_cb2).Run(); // Print 3.

Binding A Class Method

The first argument to bind is the member function to call, the second is the object on which to call it.

class Ref : public base::RefCountedThreadSafe<Ref> { public: int Foo() { return 3; } }; scoped_refptr<Ref> ref = new Ref(); base::Callback<void()> ref_cb = base::Bind(&Ref::Foo, ref); LOG(INFO) << ref_cb.Run(); // Prints out 3.

By default the object must support RefCounted or you will get a compiler error. If you‘re passing between threads, be sure it’s RefCountedThreadSafe! See “Advanced binding of member functions” below if you don't want to use reference counting.

Running A Callback

Callbacks can be run with their Run method, which has the same signature as the template argument to the callback. Note that base::OnceCallback::Run consumes the callback object and can only be invoked on a callback rvalue.

void DoSomething(const base::Callback<void(int, std::string)>& callback) { callback.Run(5, "hello"); } void DoSomethingOther(base::OnceCallback<void(int, std::string)> callback) { std::move(callback).Run(5, "hello"); }

RepeatingCallbacks can be run more than once (they don't get deleted or marked when run). However, this precludes using base::Passed (see below).

void DoSomething(const base::RepeatingCallback<double(double)>& callback) { double myresult = callback.Run(3.14159); myresult += callback.Run(2.71828); }

If running a callback could result in its own destruction (e.g., if the callback recipient deletes the object the callback is a member of), the callback should be moved before it can be safely invoked. (Note that this is only an issue for RepeatingCallbacks, because a OnceCallback always has to be moved for execution.)

void Foo::RunCallback() { std::move(&foo_deleter_callback_).Run(); }

Creating a Callback That Does Nothing

Sometimes you need a callback that does nothing when run (e.g. test code that doesn't care to be notified about certain types of events). It may be tempting to pass a default-constructed callback of the right type:

using MyCallback = base::OnceCallback<void(bool arg)>; void MyFunction(MyCallback callback) { std::move(callback).Run(true); // Uh oh... } ... MyFunction(MyCallback()); // ...this will crash when Run()!

Default-constructed callbacks are null, and thus cannot be Run(). Instead, use base::DoNothing():

...
MyFunction(base::DoNothing()); // Can be Run(), will no-op

base::DoNothing() can be passed for any OnceCallback or RepeatingCallback that returns void.

Implementation-wise, base::DoNothing() is actually a functor which produces a callback from operator(). This makes it unusable when trying to bind other arguments to it. Normally, the only reason to bind arguments to DoNothing() is to manage object lifetimes, and in these cases, you should strive to use idioms like DeleteSoon(), ReleaseSoon(), or RefCountedDeleteOnSequence instead. If you truly need to bind an argument to DoNothing(), or if you need to explicitly create a callback object (because implicit conversion through operator()() won't compile), you can instantiate directly:

// Binds |foo_ptr| to a no-op OnceCallback takes a scoped_refptr<Foo>.
// ANTIPATTERN WARNING: This should likely be changed to ReleaseSoon()!
base::Bind(base::DoNothing::Once<scoped_refptr<Foo>>(), foo_ptr);

Passing Unbound Input Parameters

Unbound parameters are specified at the time a callback is Run(). They are specified in the base::Callback template type:

void MyFunc(int i, const std::string& str) {} base::Callback<void(int, const std::string&)> cb = base::Bind(&MyFunc); cb.Run(23, "hello, world");

Passing Bound Input Parameters

Bound parameters are specified when you create the callback as arguments to base::Bind(). They will be passed to the function and the Run()ner of the callback doesn‘t see those values or even know that the function it’s calling.

void MyFunc(int i, const std::string& str) {} base::Callback<void()> cb = base::Bind(&MyFunc, 23, "hello world"); cb.Run();

A callback with no unbound input parameters (base::Callback<void()>) is called a base::Closure. So we could have also written:

base::Closure cb = base::Bind(&MyFunc, 23, "hello world");

When calling member functions, bound parameters just go after the object pointer.

base::Closure cb = base::Bind(&MyClass::MyFunc, this, 23, "hello world");

Partial Binding Of Parameters (Currying)

You can specify some parameters when you create the callback, and specify the rest when you execute the callback.

When calling a function bound parameters are first, followed by unbound parameters.

void ReadIntFromFile(const std::string& filename, base::OnceCallback<void(int)> on_read); void DisplayIntWithPrefix(const std::string& prefix, int result) { LOG(INFO) << prefix << result; } void AnotherFunc(const std::string& file) { ReadIntFromFile(file, base::BindOnce(&DisplayIntWithPrefix, "MyPrefix: ")); };

This technique is known as Currying. It should be used in lieu of creating an adapter class that holds the bound arguments. Notice also that the "MyPrefix: " argument is actually a const char*, while DisplayIntWithPrefix actually wants a const std::string&. Like normal function dispatch, base::Bind, will coerce parameter types if possible.

Avoiding Copies With Callback Parameters

A parameter of base::BindRepeating() or base::BindOnce() is moved into its internal storage if it is passed as a rvalue.

std::vector<int> v = {1, 2, 3}; // |v| is moved into the internal storage without copy. base::Bind(&Foo, std::move(v));
// The vector is moved into the internal storage without copy.
base::Bind(&Foo, std::vector<int>({1, 2, 3}));

Arguments bound with base::BindOnce() are always moved, if possible, to the target function. A function parameter that is passed by value and has a move constructor will be moved instead of copied. This makes it easy to use move-only types with base::BindOnce().

In contrast, arguments bound with base::BindRepeating() are only moved to the target function if the argument is bound with base::Passed().

DANGER: A base::RepeatingCallback can only be run once if arguments were bound with base::Passed(). For this reason, avoid base::Passed(). If you know a callback will only be called once, prefer to refactor code to work with base::OnceCallback instead.

Avoid using base::Passed() with base::BindOnce(), as std::move() does the same thing and is more familiar.

void Foo(std::unique_ptr<int>) {} auto p = std::make_unique<int>(42); // |p| is moved into the internal storage of Bind(), and moved out to |Foo|. base::BindOnce(&Foo, std::move(p)); base::BindRepeating(&Foo, base::Passed(&p)); // Ok, but subtle. base::BindRepeating(&Foo, base::Passed(std::move(p))); // Ok, but subtle.

Quick reference for advanced binding

Binding A Class Method With Weak Pointers

base::Bind(&MyClass::Foo, GetWeakPtr());

The callback will not be run if the object has already been destroyed. DANGER: weak pointers are not threadsafe, so don't use this when passing between threads!

To make a weak pointer, you would typically create a base::WeakPtrFactory<Foo> member at the bottom (to ensure it's destroyed last) of class Foo, then call weak_factory_.GetWeakPtr().

Binding A Class Method With Manual Lifetime Management

base::Bind(&MyClass::Foo, base::Unretained(this));

This disables all lifetime management on the object. You're responsible for making sure the object is alive at the time of the call. You break it, you own it!

Binding A Class Method And Having The Callback Own The Class

MyClass* myclass = new MyClass; base::Bind(&MyClass::Foo, base::Owned(myclass));

The object will be deleted when the callback is destroyed, even if it's not run (like if you post a task during shutdown). Potentially useful for “fire and forget” cases.

Smart pointers (e.g. std::unique_ptr<>) are also supported as the receiver.

std::unique_ptr<MyClass> myclass(new MyClass); base::Bind(&MyClass::Foo, std::move(myclass));

Ignoring Return Values

Sometimes you want to call a function that returns a value in a callback that doesn't expect a return value.

int DoSomething(int arg) { cout << arg << endl; } base::Callback<void(int)> cb = base::Bind(IgnoreResult(&DoSomething));

Quick reference for binding parameters to Bind()

Bound parameters are specified as arguments to base::Bind() and are passed to the function. A callback with no parameters or no unbound parameters is called a base::Closure (base::Callback<void()> and base::Closure are the same thing).

Passing Parameters Owned By The Callback

void Foo(int* arg) { cout << *arg << endl; } int* pn = new int(1); base::Closure foo_callback = base::Bind(&foo, base::Owned(pn));

The parameter will be deleted when the callback is destroyed, even if it's not run (like if you post a task during shutdown).

Passing Parameters As A unique_ptr

void TakesOwnership(std::unique_ptr<Foo> arg) {} auto f = std::make_unique<Foo>(); // f becomes null during the following call. base::OnceClosure cb = base::BindOnce(&TakesOwnership, std::move(f));

Ownership of the parameter will be with the callback until the callback is run, and then ownership is passed to the callback function. This means the callback can only be run once. If the callback is never run, it will delete the object when it's destroyed.

Passing Parameters As A scoped_refptr

void TakesOneRef(scoped_refptr<Foo> arg) {} scoped_refptr<Foo> f(new Foo); base::Closure cb = base::Bind(&TakesOneRef, f);

This should “just work.” The closure will take a reference as long as it is alive, and another reference will be taken for the called function.

void DontTakeRef(Foo* arg) {} scoped_refptr<Foo> f(new Foo); base::Closure cb = base::Bind(&DontTakeRef, base::RetainedRef(f));

base::RetainedRef holds a reference to the object and passes a raw pointer to the object when the Callback is run.

Passing Parameters By Reference

Const references are copied unless base::ConstRef is used. Example:

void foo(const int& arg) { printf("%d %p\n", arg, &arg); } int n = 1; base::Closure has_copy = base::Bind(&foo, n); base::Closure has_ref = base::Bind(&foo, base::ConstRef(n)); n = 2; foo(n); // Prints "2 0xaaaaaaaaaaaa" has_copy.Run(); // Prints "1 0xbbbbbbbbbbbb" has_ref.Run(); // Prints "2 0xaaaaaaaaaaaa"

Normally parameters are copied in the closure. DANGERbase::ConstRef stores a const reference instead, referencing the original parameter. This means that you must ensure the object outlives the callback!

Implementation notes

Where Is This Design From:

The design of base::Callback and base::Bind is heavily influenced by C++'s tr1::function / tr1::bind, and by the “Google Callback” system used inside Google.

Customizing the behavior

There are several injection points that controls binding behavior from outside of its implementation.

namespace base {

template <typename Receiver> struct IsWeakReceiver { static constexpr bool value = false; }; template <typename Obj> struct UnwrapTraits { template <typename T> T&& Unwrap(T&& obj) { return std::forward<T>(obj); } }; } // namespace base

If base::IsWeakReceiver<Receiver>::value is true on a receiver of a method, base::Bind checks if the receiver is evaluated to true and cancels the invocation if it's evaluated to false. You can specialize base::IsWeakReceiver to make an external smart pointer as a weak pointer.

base::UnwrapTraits<BoundObject>::Unwrap() is called for each bound arguments right before base::Callback calls the target function. You can specialize this to define an argument wrapper such as base::Unretainedbase::ConstRefbase::Ownedbase::RetainedRef and base::Passed.

How The Implementation Works:

There are three main components to the system:

  1. The base::Callback<> classes.
  2. The base::Bind() functions.
  3. The arguments wrappers (e.g., base::Unretained() and base::ConstRef()).

The Callback classes represent a generic function pointer. Internally, it stores a refcounted piece of state that represents the target function and all its bound parameters. The base::Callback constructor takes a base::BindStateBase*, which is upcasted from a base::BindState<>. In the context of the constructor, the static type of this base::BindState<> pointer uniquely identifies the function it is representing, all its bound parameters, and a Run() method that is capable of invoking the target.

base::Bind() creates the base::BindState<> that has the full static type, and erases the target function type as well as the types of the bound parameters. It does this by storing a pointer to the specific Run() function, and upcasting the state of base::BindState<>* to a base::BindStateBase*. This is safe as long as this BindStateBase pointer is only used with the stored Run() pointer.

To base::BindState<> objects are created inside the base::Bind() functions. These functions, along with a set of internal templates, are responsible for

  • Unwrapping the function signature into return type, and parameters
  • Determining the number of parameters that are bound
  • Creating the BindState storing the bound parameters
  • Performing compile-time asserts to avoid error-prone behavior
  • Returning a Callback<> with an arity matching the number of unbound parameters and that knows the correct refcounting semantics for the target object if we are binding a method.

The base::Bind functions do the above using type-inference and variadic templates.

By default base::Bind() will store copies of all bound parameters, and attempt to refcount a target object if the function being bound is a class method. These copies are created even if the function takes parameters as const references. (Binding to non-const references is forbidden, see bind.h.)

To change this behavior, we introduce a set of argument wrappers (e.g., base::Unretained(), and base::ConstRef()). These are simple container templates that are passed by value, and wrap a pointer to argument. See the file-level comment in base/bind_helpers.h for more info.

These types are passed to the Unwrap() functions to modify the behavior of base::Bind(). The Unwrap() functions change behavior by doing partial specialization based on whether or not a parameter is a wrapper type.

base::ConstRef() is similar to tr1::crefbase::Unretained() is specific to Chromium.

Missing Functionality

  • Binding arrays to functions that take a non-const pointer. Example:
void Foo(const char* ptr); void Bar(char* ptr); base::Bind(&Foo, "test"); base::Bind(&Bar, "test"); // This fails because ptr is not const.
  • In case of partial binding of parameters a possibility of having unbound parameters before bound parameters. Example:
void Foo(int x, bool y); base::Bind(&Foo, _1, false); // _1 is a placeholder.

If you are thinking of forward declaring base::Callback in your own header file, please include “base/callback_forward.h” instead.

转载于:https://www.cnblogs.com/huangguanyuan/p/9673458.html

[New Thread 0x7fffe62b0640 (LWP 93378)] [INFO] [1752816588.865113102] [imu_camera_sync]: IMU and Camera Sync Node started [INFO] [1752816588.865206898] [imu_camera_sync]: Waiting for synchronized IMU and Image data... Thread 1 "imu_camera_sync" received signal SIGSEGV, Segmentation fault. 0x00005555555a10c2 in ImuCameraSync::imu_callback(std::shared_ptr<sensor_msgs::msg::Imu_<std::allocator<void> > >) () (gdb) bt #0 0x00005555555a10c2 in ImuCameraSync::imu_callback(std::shared_ptr<sensor_msgs::msg::Imu_<std::allocator<void> > >) () #1 0x0000555555584107 in std::_Function_handler<void (std::shared_ptr<sensor_msgs::msg::Imu_<std::allocator<void> > >), std::_Bind<void (ImuCameraSync::*(ImuCameraSync*, std::_Placeholder<1>))(std::shared_ptr<sensor_msgs::msg::Imu_<std::allocator<void> > >)> >::_M_invoke(std::_Any_data const&, std::shared_ptr<sensor_msgs::msg::Imu_<std::allocator<void> > >&&) () #2 0x0000555555583043 in std::__detail::__variant::__gen_vtable_impl<std::__detail::__variant::_Multi_array<std::__detail::__variant::__deduce_visit_result<void> (*)(rclcpp::AnySubscriptionCallback<sensor_msgs::msg::Imu_<std::allocator<void> >, std::allocator<void> >::dispatch(std::shared_ptr<sensor_msgs::msg::Imu_<std::allocator<void> > >, rclcpp::MessageInfo const&)::{lambda(auto:1&&)#1}&&, std::variant<std::function<void (sensor_msgs::msg::Imu_<std::allocator<void> > const&)>, std::function<void (sensor_msgs::msg::Imu_<std::allocator<void> > const&, rclcpp::MessageInfo const&)>, std::function<void (rclcpp::SerializedMessage const&)>, std::function<void (rclcpp::SerializedMessage const&, rclcpp::MessageInfo const&)>, std::function<void (std::unique_ptr<sensor_msgs::msg::Imu_<std::allocator<void> >, std::default_delete<sensor_msgs::msg::Imu_<std::allocator<void> > > >)>, std::function<void (std::unique_ptr<sensor_msgs::msg::Imu_<std::allocator<void> >, std::default_delete<sensor_msgs::msg::Imu_<std::allocator<void> > > >, rclcpp::MessageInfo const&)>, std::function<void (std::unique_ptr<rclcpp::SerializedMessage, std::default_delete<rclcpp::SerializedMessage> >)>, std::function<void (std::unique_ptr<rclcpp::SerializedMessage, std::default_delete<rclcpp::Seri--Type <RET> for more, q to quit, c to continue without paging--q Quit 怎么看到具体的代码位置阿?
07-19
import _ = require("../index"); declare module "../index" { interface LoDashStatic { /** * Attempts to invoke func, returning either the result or the caught error object. Any additional arguments * are provided to func when it’s invoked. * * @param func The function to attempt. * @return Returns the func result or error object. */ attempt<TResult>(func: (...args: any[]) => TResult, ...args: any[]): TResult | Error; } interface LoDashImplicitWrapper<TValue> { /** * @see _.attempt */ attempt<TResult>(...args: any[]): TResult | Error; } interface LoDashExplicitWrapper<TValue> { /** * @see _.attempt */ attempt<TResult>(...args: any[]): ExpChain<TResult | Error>; } interface LoDashStatic { /** * Binds methods of an object to the object itself, overwriting the existing method. Method names may be * specified as individual arguments or as arrays of method names. If no method names are provided all * enumerable function properties, own and inherited, of object are bound. * * Note: This method does not set the "length" property of bound functions. * * @param object The object to bind and assign the bound methods to. * @param methodNames The object method names to bind, specified as individual method names or arrays of * method names. * @return Returns object. */ bindAll<T>(object: T, ...methodNames: Array<Many<string>>): T; } interface LoDashImplicitWrapper<TValue> { /** * @see _.bindAll */ bindAll(...methodNames: Array<Many<string>>): this; } interface LoDashExplicitWrapper<TValue> { /** * @see _.bindAll */ bindAll(...methodNames: Array<Many<string>>): this; } interface LoDashStatic { /** * Creates a function that iterates over `pairs` and invokes the corresponding * function of the first predicate to return truthy. The predicate-function * pairs are invoked with the `this` binding and arguments of the created * function. * * @since 4.0.0 * @category Util * @param pairs The predicate-function pairs. * @returns Returns the new composite function. * @example * * var func = _.cond([ * [_.matches({ 'a': 1 }), _.constant('matches A')], * [_.conforms({ 'b': _.isNumber }), _.constant('matches B')], * [_.stubTrue, _.constant('no match')] * ]); * * func({ 'a': 1, 'b': 2 }); * // => 'matches A' * * func({ 'a': 0, 'b': 1 }); * // => 'matches B' * * func({ 'a': '1', 'b': '2' }); * // => 'no match' */ cond<T, R>(pairs: Array<CondPair<T, R>>): (Target: T) => R; } type ConformsPredicateObject<T> = { [P in keyof T]: T[P] extends (arg: infer A) => any ? A : any }; interface LoDashStatic { /** * Creates a function that invokes the predicate properties of `source` with the corresponding * property values of a given object, returning true if all predicates return truthy, else false. */ conforms<T>(source: ConformsPredicateObject<T>): (value: T) => boolean; } interface LoDashImplicitWrapper<TValue> { /** * @see _.conforms */ conforms(): Function<(value: ConformsPredicateObject<TValue>) => boolean>; } interface LoDashExplicitWrapper<TValue> { /** * @see _.conforms */ conforms(): FunctionChain<(value: ConformsPredicateObject<TValue>) => boolean>; } interface LoDashStatic { /** * Creates a function that returns value. * * @param value The value to return from the new function. * @return Returns the new function. */ constant<T>(value: T): () => T; } interface LoDashImplicitWrapper<TValue> { /** * @see _.constant */ constant(): Function<() => TValue>; } interface LoDashExplicitWrapper<TValue> { /** * @see _.constant */ constant(): FunctionChain<() => TValue>; } interface LoDashStatic { /** * Checks `value` to determine whether a default value should be returned in * its place. The `defaultValue` is returned if `value` is `NaN`, `null`, * or `undefined`. * * @param value The value to check. * @param defaultValue The default value. * @returns Returns the resolved value. */ defaultTo<T>(value: T | null | undefined, defaultValue: T): T; /** * @see _.defaultTo */ defaultTo<T, TDefault>(value: T | null | undefined, defaultValue: TDefault): T | TDefault; } interface LoDashImplicitWrapper<TValue> { /** * @see _.defaultTo */ defaultTo(defaultValue: TValue): TValue; /** * @see _.defaultTo */ defaultTo<TDefault>(defaultValue: TDefault): TValue extends null | undefined ? TDefault : TValue | TDefault; } interface LoDashExplicitWrapper<TValue> { /** * @see _.defaultTo */ defaultTo(defaultValue: TValue): ExpChain<TValue>; /** * @see _.defaultTo */ defaultTo<TDefault>(defaultValue: TDefault): ExpChain<TValue extends null | undefined ? TDefault : TValue | TDefault>; } interface LoDashStatic { /** * Creates a function that returns the result of invoking the provided functions with the this binding of the * created function, where each successive invocation is supplied the return value of the previous. * * @param funcs Functions to invoke. * @return Returns the new function. */ flow<A extends any[], R1, R2, R3, R4, R5, R6, R7>(f1: (...args: A) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6, f7: (a: R6) => R7): (...args: A) => R7; /** * @see _.flow */ flow<A extends any[], R1, R2, R3, R4, R5, R6, R7>(f1: (...args: A) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6, f7: (a: R6) => R7, ...func: Array<Many<(a: any) => any>>): (...args: A) => any; /** * @see _.flow */ flow<A extends any[], R1, R2, R3, R4, R5, R6>(f1: (...args: A) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6): (...args: A) => R6; /** * @see _.flow */ flow<A extends any[], R1, R2, R3, R4, R5>(f1: (...args: A) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5): (...args: A) => R5; /** * @see _.flow */ flow<A extends any[], R1, R2, R3, R4>(f1: (...args: A) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4): (...args: A) => R4; /** * @see _.flow */ flow<A extends any[], R1, R2, R3>(f1: (...args: A) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3): (...args: A) => R3; /** * @see _.flow */ flow<A extends any[], R1, R2>(f1: (...args: A) => R1, f2: (a: R1) => R2): (...args: A) => R2; /** * @see _.flow */ flow(...func: Array<Many<(...args: any[]) => any>>): (...args: any[]) => any; } interface Function<T extends (...arg: any) => any> { /** * @see _.flow */ flow<R2, R3, R4, R5, R6, R7>(f2: (a: ReturnType<T>) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6, f7: (a: R6) => R7): Function<(...args: Parameters<T>) => R7>; /** * @see _.flow */ flow<R2, R3, R4, R5, R6, R7>(f2: (a: ReturnType<T>) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6, f7: (a: R6) => R7, ...func: Array<Many<(a: any) => any>>): Function<(...args: Parameters<T>) => any>; /** * @see _.flow */ flow<R2, R3, R4, R5, R6>(f2: (a: ReturnType<T>) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6): Function<(...args: Parameters<T>) => R6>; /** * @see _.flow */ flow<R2, R3, R4, R5>(f2: (a: ReturnType<T>) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5): Function<(...args: Parameters<T>) => R5>; /** * @see _.flow */ flow<R2, R3, R4>(f2: (a: ReturnType<T>) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4): Function<(...args: Parameters<T>) => R4>; /** * @see _.flow */ flow<R2, R3>(f2: (a: ReturnType<T>) => R2, f3: (a: R2) => R3): Function<(...args: Parameters<T>) => R3>; /** * @see _.flow */ flow<R2>(f2: (a: ReturnType<T>) => R2): Function<(...args: Parameters<T>) => R2>; /** * @see _.flow */ flow(...func: Array<Many<(...args: any[]) => any>>): Function<(...args: any[]) => any>; } interface FunctionChain<T> { /** * @see _.flow */ flow<R2, R3, R4, R5, R6, R7>(f2: (a: ReturnType<T>) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6, f7: (a: R6) => R7): FunctionChain<(...args: Parameters<T>) => R7>; /** * @see _.flow */ flow<R2, R3, R4, R5, R6, R7>(f2: (a: ReturnType<T>) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6, f7: (a: R6) => R7, ...func: Array<Many<(a: any) => any>>): FunctionChain<(...args: Parameters<T>) => any>; /** * @see _.flow */ flow<R2, R3, R4, R5, R6>(f2: (a: ReturnType<T>) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6): FunctionChain<(...args: Parameters<T>) => R6>; /** * @see _.flow */ flow<R2, R3, R4, R5>(f2: (a: ReturnType<T>) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5): FunctionChain<(...args: Parameters<T>) => R5>; /** * @see _.flow */ flow<R2, R3, R4>(f2: (a: ReturnType<T>) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4): FunctionChain<(...args: Parameters<T>) => R4>; /** * @see _.flow */ flow<R2, R3>(f2: (a: ReturnType<T>) => R2, f3: (a: R2) => R3): FunctionChain<(...args: Parameters<T>) => R3>; /** * @see _.flow */ flow<R2>(f2: (a: ReturnType<T>) => R2): FunctionChain<(...args: Parameters<T>) => R2>; /** * @see _.flow */ flow(...func: Array<Many<(...args: any[]) => any>>): FunctionChain<(...args: any[]) => any>; } interface LoDashStatic { /** * This method is like _.flow except that it creates a function that invokes the provided functions from right * to left. * * @param funcs Functions to invoke. * @return Returns the new function. */ flowRight<A extends any[], R1, R2, R3, R4, R5, R6, R7>(f7: (a: R6) => R7, f6: (a: R5) => R6, f5: (a: R4) => R5, f4: (a: R3) => R4, f3: (a: R2) => R3, f2: (a: R1) => R2, f1: (...args: A) => R1): (...args: A) => R7; /** * @see _.flowRight */ flowRight<A extends any[], R1, R2, R3, R4, R5, R6>(f6: (a: R5) => R6, f5: (a: R4) => R5, f4: (a: R3) => R4, f3: (a: R2) => R3, f2: (a: R1) => R2, f1: (...args: A) => R1): (...args: A) => R6; /** * @see _.flowRight */ flowRight<A extends any[], R1, R2, R3, R4, R5>(f5: (a: R4) => R5, f4: (a: R3) => R4, f3: (a: R2) => R3, f2: (a: R1) => R2, f1: (...args: A) => R1): (...args: A) => R5; /** * @see _.flowRight */ flowRight<A extends any[], R1, R2, R3, R4>(f4: (a: R3) => R4, f3: (a: R2) => R3, f2: (a: R1) => R2, f1: (...args: A) => R1): (...args: A) => R4; /** * @see _.flowRight */ flowRight<A extends any[], R1, R2, R3>(f3: (a: R2) => R3, f2: (a: R1) => R2, f1: (...args: A) => R1): (...args: A) => R3; /** * @see _.flowRight */ flowRight<A extends any[], R1, R2>(f2: (a: R1) => R2, f1: (...args: A) => R1): (...args: A) => R2; /** * @see _.flowRight */ flowRight(...func: Array<Many<(...args: any[]) => any>>): (...args: any[]) => any; } interface Function<T> { /** * @see _.flowRight */ flowRight<A extends any[], R1, R2, R3, R4, R5>(f6: (a: R5) => Parameters<T>["0"], f5: (a: R4) => R5, f4: (a: R3) => R4, f3: (a: R2) => R3, f2: (a: R1) => R2, f1: (...args: A) => R1): Function<(...args: A) => ReturnType<T>>; /** * @see _.flowRight */ flowRight<A extends any[], R1, R2, R3, R4>(f5: (a: R4) => Parameters<T>["0"], f4: (a: R3) => R4, f3: (a: R2) => R3, f2: (a: R1) => R2, f1: (...args: A) => R1): Function<(...args: A) => ReturnType<T>>; /** * @see _.flowRight */ flowRight<A extends any[], R1, R2, R3>(f4: (a: R3) => Parameters<T>["0"], f3: (a: R2) => R3, f2: (a: R1) => R2, f1: (...args: A) => R1): Function<(...args: A) => ReturnType<T>>; /** * @see _.flowRight */ flowRight<A extends any[], R1, R2>(f3: (a: R2) => Parameters<T>["0"], f2: (a: R1) => R2, f1: (...args: A) => R1): Function<(...args: A) => ReturnType<T>>; /** * @see _.flowRight */ flowRight<A extends any[], R1>(f2: (a: R1) => Parameters<T>["0"], f1: (...args: A) => R1): Function<(...args: A) => ReturnType<T>>; /** * @see _.flowRight */ flowRight<A extends any[]>(f1: (...args: A) => Parameters<T>["0"]): Function<(...args: A) => ReturnType<T>>; /** * @see _.flowRight */ flowRight(...func: Array<Many<(...args: any[]) => any>>): Function<(...args: any[]) => any>; } interface FunctionChain<T> { /** * @see _.flowRight */ flowRight<A extends any[], R1, R2, R3, R4, R5>(f6: (a: R5) => Parameters<T>["0"], f5: (a: R4) => R5, f4: (a: R3) => R4, f3: (a: R2) => R3, f2: (a: R1) => R2, f1: (...args: A) => R1): FunctionChain<(...args: A) => ReturnType<T>>; /** * @see _.flowRight */ flowRight<A extends any[], R1, R2, R3, R4>(f5: (a: R4) => Parameters<T>["0"], f4: (a: R3) => R4, f3: (a: R2) => R3, f2: (a: R1) => R2, f1: (...args: A) => R1): FunctionChain<(...args: A) => ReturnType<T>>; /** * @see _.flowRight */ flowRight<A extends any[], R1, R2, R3>(f4: (a: R3) => Parameters<T>["0"], f3: (a: R2) => R3, f2: (a: R1) => R2, f1: (...args: A) => R1): FunctionChain<(...args: A) => ReturnType<T>>; /** * @see _.flowRight */ flowRight<A extends any[], R1, R2>(f3: (a: R2) => Parameters<T>["0"], f2: (a: R1) => R2, f1: (...args: A) => R1): FunctionChain<(...args: A) => ReturnType<T>>; /** * @see _.flowRight */ flowRight<A extends any[], R1>(f2: (a: R1) => Parameters<T>["0"], f1: (...args: A) => R1): FunctionChain<(...args: A) => ReturnType<T>>; /** * @see _.flowRight */ flowRight<A extends any[]>(f1: (...args: A) => Parameters<T>["0"]): FunctionChain<(...args: A) => ReturnType<T>>; /** * @see _.flowRight */ flowRight(...func: Array<Many<(...args: any[]) => any>>): FunctionChain<(...args: any[]) => any>; } interface LoDashStatic { /** * This method returns the first argument provided to it. * * @param value Any value. * @return Returns value. */ identity<T>(value: T): T; /** * @see _.identity */ identity(): undefined; } interface LoDashImplicitWrapper<TValue> { /** * @see _.identity */ identity(): TValue; } interface LoDashExplicitWrapper<TValue> { /** * @see _.identity */ identity(): this; } interface LoDashStatic { /** * Creates a function that invokes `func` with the arguments of the created * function. If `func` is a property name the created callback returns the * property value for a given element. If `func` is an object the created * callback returns `true` for elements that contain the equivalent object properties, otherwise it returns `false`. * * @category Util * @param [func=_.identity] The value to convert to a callback. * @returns Returns the callback. * @example * * var users = [ * { 'user': 'barney', 'age': 36 }, * { 'user': 'fred', 'age': 40 } * ]; * * // create custom iteratee shorthands * _.iteratee = _.wrap(_.iteratee, function(callback, func) { * var p = /^(\S+)\s*([<>])\s*(\S+)$/.exec(func); * return !p ? callback(func) : function(object) { * return (p[2] == '>' ? object[p[1]] > p[3] : object[p[1]] < p[3]); * }; * }); * * _.filter(users, 'age > 36'); * // => [{ 'user': 'fred', 'age': 40 }] */ iteratee<TFunction extends (...args: any[]) => any>(func: TFunction): TFunction; /** * @see _.iteratee */ iteratee(func: string | object): (...args: any[]) => any; } interface Function<T extends (...args: any) => any> { /** * @see _.iteratee */ iteratee(): Function<T>; } interface Collection<T> { /** * @see _.iteratee */ iteratee(): Function<(o: object) => boolean>; } interface Object<T> { /** * @see _.iteratee */ iteratee(): Function<(o: T) => boolean>; } interface String { /** * @see _.iteratee */ iteratee(): Function<(o: object) => any>; } interface FunctionChain<T extends (...args: any) => any> { /** * @see _.iteratee */ iteratee(): FunctionChain<T>; } interface CollectionChain<T> { /** * @see _.iteratee */ iteratee(): FunctionChain<(o: object) => boolean>; } interface ObjectChain<T> { /** * @see _.iteratee */ iteratee(): FunctionChain<(o: T) => boolean>; } interface StringChain { /** * @see _.iteratee */ iteratee(): FunctionChain<(o: object) => any>; } interface StringNullableChain { /** * @see _.iteratee */ iteratee(): FunctionChain<(o: object) => any>; } interface LoDashStatic { /** * Creates a function that performs a deep comparison between a given object and source, returning true if the * given object has equivalent property values, else false. * * Note: This method supports comparing arrays, booleans, Date objects, numbers, Object objects, regexes, and * strings. Objects are compared by their own, not inherited, enumerable properties. For comparing a single own * or inherited property value see _.matchesProperty. * * @param source The object of property values to match. * @return Returns the new function. */ matches<T>(source: T): (value: any) => boolean; /** * @see _.matches */ matches<T, V>(source: T): (value: V) => boolean; } interface LoDashImplicitWrapper<TValue> { /** * @see _.matches */ matches<V>(): Function<(value: V) => boolean>; } interface LoDashExplicitWrapper<TValue> { /** * @see _.matches */ matches<V>(): FunctionChain<(value: V) => boolean>; } interface LoDashStatic { /** * Creates a function that compares the property value of path on a given object to value. * * Note: This method supports comparing arrays, booleans, Date objects, numbers, Object objects, regexes, and * strings. Objects are compared by their own, not inherited, enumerable properties. * * @param path The path of the property to get. * @param srcValue The value to match. * @return Returns the new function. */ matchesProperty<T>(path: PropertyPath, srcValue: T): (value: any) => boolean; /** * @see _.matchesProperty */ matchesProperty<T, V>(path: PropertyPath, srcValue: T): (value: V) => boolean; } interface LoDashImplicitWrapper<TValue> { /** * @see _.matchesProperty */ matchesProperty<SrcValue>(srcValue: SrcValue): Function<(value: any) => boolean>; /** * @see _.matchesProperty */ matchesProperty<SrcValue, Value>(srcValue: SrcValue): Function<(value: Value) => boolean>; } interface LoDashExplicitWrapper<TValue> { /** * @see _.matchesProperty */ matchesProperty<SrcValue>(srcValue: SrcValue): FunctionChain<(value: any) => boolean>; /** * @see _.matchesProperty */ matchesProperty<SrcValue, Value>(srcValue: SrcValue): FunctionChain<(value: Value) => boolean>; } interface LoDashStatic { /** * Creates a function that invokes the method at path on a given object. Any additional arguments are provided * to the invoked method. * * @param path The path of the method to invoke. * @param args The arguments to invoke the method with. * @return Returns the new function. */ method(path: PropertyPath, ...args: any[]): (object: any) => any; } interface LoDashImplicitWrapper<TValue> { /** * @see _.method */ method(...args: any[]): Function<(object: any) => any>; } interface LoDashExplicitWrapper<TValue> { /** * @see _.method */ method(...args: any[]): FunctionChain<(object: any) => any>; } interface LoDashStatic { /** * The opposite of _.method; this method creates a function that invokes the method at a given path on object. * Any additional arguments are provided to the invoked method. * * @param object The object to query. * @param args The arguments to invoke the method with. * @return Returns the new function. */ methodOf(object: object, ...args: any[]): (path: PropertyPath) => any; } interface LoDashImplicitWrapper<TValue> { /** * @see _.methodOf */ methodOf(...args: any[]): LoDashImplicitWrapper<(path: PropertyPath) => any>; } interface LoDashExplicitWrapper<TValue> { /** * @see _.methodOf */ methodOf(...args: any[]): LoDashExplicitWrapper<(path: PropertyPath) => any>; } interface MixinOptions { /** * @see _.chain */ chain?: boolean; } interface LoDashStatic { /** * Adds all own enumerable function properties of a source object to the destination object. If object is a * function then methods are added to its prototype as well. * * Note: Use _.runInContext to create a pristine lodash function to avoid conflicts caused by modifying * the original. * * @param object The destination object. * @param source The object of functions to add. * @param options The options object. * @param options.chain Specify whether the functions added are chainable. * @return Returns object. */ mixin<TObject>(object: TObject, source: Dictionary<(...args: any[]) => any>, options?: MixinOptions): TObject; /** * @see _.mixin */ mixin<TResult>(source: Dictionary<(...args: any[]) => any>, options?: MixinOptions): LoDashStatic; } interface LoDashImplicitWrapper<TValue> { /** * @see _.mixin */ mixin(source: Dictionary<(...args: any[]) => any>, options?: MixinOptions): this; /** * @see _.mixin */ mixin(options?: MixinOptions): LoDashImplicitWrapper<LoDashStatic>; } interface LoDashExplicitWrapper<TValue> { /** * @see _.mixin */ mixin(source: Dictionary<(...args: any[]) => any>, options?: MixinOptions): this; /** * @see _.mixin */ mixin(options?: MixinOptions): LoDashExplicitWrapper<LoDashStatic>; } interface LoDashStatic { /** * Reverts the _ variable to its previous value and returns a reference to the lodash function. * * @return Returns the lodash function. */ noConflict(): typeof _; } interface LoDashImplicitWrapper<TValue> { /** * @see _.noConflict */ noConflict(): typeof _; } interface LoDashExplicitWrapper<TValue> { /** * @see _.noConflict */ noConflict(): LoDashExplicitWrapper<typeof _>; } interface LoDashStatic { /** * A no-operation function that returns undefined regardless of the arguments it receives. * * @return undefined */ noop(...args: any[]): void; } interface LoDashImplicitWrapper<TValue> { /** * @see _.noop */ noop(...args: any[]): void; } interface LoDashExplicitWrapper<TValue> { /** * @see _.noop */ noop(...args: any[]): PrimitiveChain<undefined>; } interface LoDashStatic { /** * Creates a function that returns its nth argument. * * @param n The index of the argument to return. * @return Returns the new function. */ nthArg(n?: number): (...args: any[]) => any; } interface LoDashImplicitWrapper<TValue> { /** * @see _.nthArg */ nthArg(): Function<(...args: any[]) => any>; } interface LoDashExplicitWrapper<TValue> { /** * @see _.nthArg */ nthArg(): FunctionChain<(...args: any[]) => any>; } interface LoDashStatic { /** * Creates a function that invokes iteratees with the arguments provided to the created function and returns * their results. * * @param iteratees The iteratees to invoke. * @return Returns the new function. */ over<TResult>(...iteratees: Array<Many<(...args: any[]) => TResult>>): (...args: any[]) => TResult[]; } interface Collection<T> { /** * @see _.over */ over<TResult>(...iteratees: Array<Many<(...args: any[]) => TResult>>): Function<(...args: any[]) => TResult[]>; } interface Function<T> { /** * @see _.over */ over<TResult>(...iteratees: Array<Many<(...args: any[]) => TResult>>): Function<(...args: any[]) => Array<ReturnType<T> | TResult>>; } interface CollectionChain<T> { /** * @see _.over */ over<TResult>(...iteratees: Array<Many<(...args: any[]) => TResult>>): FunctionChain<(...args: any[]) => TResult[]>; } interface FunctionChain<T> { /** * @see _.over */ over<TResult>(...iteratees: Array<Many<(...args: any[]) => TResult>>): FunctionChain<(...args: any[]) => Array<ReturnType<T> | TResult>>; } interface LoDashStatic { /** * Creates a function that checks if all of the predicates return truthy when invoked with the arguments * provided to the created function. * * @param predicates The predicates to check. * @return Returns the new function. */ overEvery<T, Result1 extends T, Result2 extends T>(...predicates: [ (arg: T) => arg is Result1, (arg: T) => arg is Result2 ]): (arg: T) => arg is Result1 & Result2; overEvery<T>(...predicates: Array<Many<(...args: T[]) => boolean>>): (...args: T[]) => boolean; } interface Collection<T> { /** * @see _.overEvery */ overEvery<TArgs>(...iteratees: Array<Many<(...args: TArgs[]) => boolean>>): Function<(...args: TArgs[]) => boolean>; } interface Function<T> { /** * @see _.overEvery */ overEvery<TArgs>(...iteratees: Array<Many<(...args: TArgs[]) => boolean>>): Function<(...args: Parameters<T> | TArgs[]) => boolean>; } interface CollectionChain<T> { /** * @see _.overEvery */ overEvery<TArgs>(...iteratees: Array<Many<(...args: TArgs[]) => boolean>>): FunctionChain<(...args: TArgs[]) => boolean>; } interface FunctionChain<T> { /** * @see _.overEvery */ overEvery<TArgs>(...iteratees: Array<Many<(...args: TArgs[]) => boolean>>): FunctionChain<(...args: Parameters<T> | TArgs[]) => boolean>; } interface LoDashStatic { /** * Creates a function that checks if any of the predicates return truthy when invoked with the arguments * provided to the created function. * * @param predicates The predicates to check. * @return Returns the new function. */ overSome<T, Result1 extends T, Result2 extends T>(...predicates: [ (arg: T) => arg is Result1, (arg: T) => arg is Result2 ]): (arg: T) => arg is Result1 | Result2; overSome<T>(...predicates: Array<Many<(...args: T[]) => boolean>>): (...args: T[]) => boolean; } interface Collection<T> { /** * @see _.overSome */ overSome<TArgs>(...iteratees: Array<Many<(...args: TArgs[]) => boolean>>): Function<(...args: TArgs[]) => boolean>; } interface Function<T> { /** * @see _.overSome */ overSome<TArgs>(...iteratees: Array<Many<(...args: TArgs[]) => boolean>>): Function<(...args: Parameters<T> | TArgs[]) => boolean>; } interface CollectionChain<T> { /** * @see _.overSome */ overSome<TArgs>(...iteratees: Array<Many<(...args: TArgs[]) => boolean>>): FunctionChain<(...args: TArgs[]) => boolean>; } interface FunctionChain<T> { /** * @see _.overSome */ overSome<TArgs>(...iteratees: Array<Many<(...args: TArgs[]) => boolean>>): FunctionChain<(...args: Parameters<T> | TArgs[]) => boolean>; } interface LoDashStatic { /** * Creates a function that returns the property value at path on a given object. * * @param path The path of the property to get. * @return Returns the new function. */ property<TObj, TResult>(path: PropertyPath): (obj: TObj) => TResult; } interface LoDashImplicitWrapper<TValue> { /** * @see _.property */ property<TObj, TResult>(): Function<(obj: TObj) => TResult>; } interface LoDashExplicitWrapper<TValue> { /** * @see _.property */ property<TObj, TResult>(): FunctionChain<(obj: TObj) => TResult>; } interface LoDashStatic { /** * The opposite of _.property; this method creates a function that returns the property value at a given path * on object. * * @param object The object to query. * @return Returns the new function. */ propertyOf<T extends {}>(object: T): (path: PropertyPath) => any; } interface LoDashImplicitWrapper<TValue> { /** * @see _.propertyOf */ propertyOf(): LoDashImplicitWrapper<(path: PropertyPath) => any>; } interface LoDashExplicitWrapper<TValue> { /** * @see _.propertyOf */ propertyOf(): LoDashExplicitWrapper<(path: PropertyPath) => any>; } interface LoDashStatic { /** * Creates an array of numbers (positive and/or negative) progressing from start up to, but not including, end. * If end is not specified it’s set to start with start then set to 0. If end is less than start a zero-length * range is created unless a negative step is specified. * * @param start The start of the range. * @param end The end of the range. * @param step The value to increment or decrement by. * @return Returns a new range array. */ range(start: number, end?: number, step?: number): number[]; /** * @see _.range */ range(end: number, index: string | number, guard: object): number[]; } interface LoDashImplicitWrapper<TValue> { /** * @see _.range */ range(end?: number, step?: number): Collection<number>; } interface LoDashExplicitWrapper<TValue> { /** * @see _.range */ range(end?: number, step?: number): CollectionChain<number>; } interface LoDashStatic { /** * This method is like `_.range` except that it populates values in * descending order. * * @category Util * @param start The start of the range. * @param end The end of the range. * @param step The value to increment or decrement by. * @returns Returns the new array of numbers. * @example * * _.rangeRight(4); * // => [3, 2, 1, 0] * * _.rangeRight(-4); * // => [-3, -2, -1, 0] * * _.rangeRight(1, 5); * // => [4, 3, 2, 1] * * _.rangeRight(0, 20, 5); * // => [15, 10, 5, 0] * * _.rangeRight(0, -4, -1); * // => [-3, -2, -1, 0] * * _.rangeRight(1, 4, 0); * // => [1, 1, 1] * * _.rangeRight(0); * // => [] */ rangeRight(start: number, end?: number, step?: number): number[]; /** * @see _.rangeRight */ rangeRight(end: number, index: string | number, guard: object): number[]; } interface LoDashImplicitWrapper<TValue> { /** * @see _.rangeRight */ rangeRight(end?: number, step?: number): Collection<number>; } interface LoDashExplicitWrapper<TValue> { /** * @see _.rangeRight */ rangeRight(end?: number, step?: number): CollectionChain<number>; } interface LoDashStatic { /** * Create a new pristine lodash function using the given context object. * * @param context The context object. * @return Returns a new lodash function. */ runInContext(context?: object): LoDashStatic; } interface LoDashImplicitWrapper<TValue> { /** * @see _.runInContext */ runInContext(): LoDashStatic; } interface LoDashStatic { /** * This method returns a new empty array. * * @returns Returns the new empty array. */ stubArray(): any[]; } interface LoDashImplicitWrapper<TValue> { /** * @see _.stubArray */ stubArray(): any[]; } interface LoDashExplicitWrapper<TValue> { /** * @see _.stubArray */ stubArray(): CollectionChain<any>; } interface LoDashStatic { /** * This method returns `false`. * * @returns Returns `false`. */ stubFalse(): false; } interface LoDashImplicitWrapper<TValue> { /** * @see _.stubFalse */ stubFalse(): false; } interface LoDashExplicitWrapper<TValue> { /** * @see _.stubFalse */ stubFalse(): PrimitiveChain<false>; } interface LoDashStatic { /** * This method returns a new empty object. * * @returns Returns the new empty object. */ stubObject(): any; } interface LoDashImplicitWrapper<TValue> { /** * @see _.stubObject */ stubObject(): any; } interface LoDashExplicitWrapper<TValue> { /** * @see _.stubObject */ stubObject(): LoDashExplicitWrapper<any>; } interface LoDashStatic { /** * This method returns an empty string. * * @returns Returns the empty string. */ stubString(): string; } interface LoDashImplicitWrapper<TValue> { /** * @see _.stubString */ stubString(): string; } interface LoDashExplicitWrapper<TValue> { /** * @see _.stubString */ stubString(): StringChain; } interface LoDashStatic { /** * This method returns `true`. * * @returns Returns `true`. */ stubTrue(): true; } interface LoDashImplicitWrapper<TValue> { /** * @see _.stubTrue */ stubTrue(): true; } interface LoDashExplicitWrapper<TValue> { /** * @see _.stubTrue */ stubTrue(): PrimitiveChain<true>; } interface LoDashStatic { /** * Invokes the iteratee function n times, returning an array of the results of each invocation. The iteratee * is invoked with one argument; (index). * * @param n The number of times to invoke iteratee. * @param iteratee The function invoked per iteration. * @return Returns the array of results. */ times<TResult>(n: number, iteratee: (num: number) => TResult): TResult[]; /** * @see _.times */ times(n: number): number[]; } interface LoDashImplicitWrapper<TValue> { /** * @see _.times */ times<TResult>(iteratee: (num: number) => TResult): TResult[]; /** * @see _.times */ times(): number[]; } interface LoDashExplicitWrapper<TValue> { /** * @see _.times */ times<TResult>(iteratee: (num: number) => TResult): CollectionChain<TResult>; /** * @see _.times */ times(): CollectionChain<number>; } interface LoDashStatic { /** * Converts `value` to a property path array. * * @category Util * @param value The value to convert. * @returns Returns the new property path array. * @example * * _.toPath('a.b.c'); * // => ['a', 'b', 'c'] * * _.toPath('a[0].b.c'); * // => ['a', '0', 'b', 'c'] * * var path = ['a', 'b', 'c'], * newPath = _.toPath(path); * * console.log(newPath); * // => ['a', 'b', 'c'] * * console.log(path === newPath); * // => false */ toPath(value: any): string[]; } interface LoDashImplicitWrapper<TValue> { /** * @see _.toPath */ toPath(): Collection<string>; } interface LoDashExplicitWrapper<TValue> { /** * @see _.toPath */ toPath(): CollectionChain<string>; } interface LoDashStatic { /** * Generates a unique ID. If prefix is provided the ID is appended to it. * * @param prefix The value to prefix the ID with. * @return Returns the unique ID. */ uniqueId(prefix?: string): string; } interface LoDashImplicitWrapper<TValue> { /** * @see _.uniqueId */ uniqueId(): string; } interface LoDashExplicitWrapper<TValue> { /** * @see _.uniqueId */ uniqueId(): StringChain; } // stubTrue interface LoDashStatic { /** * This method returns true. * * @return Returns true. */ stubTrue(): true; } interface LoDashImplicitWrapper<TValue> { /** * @see _.stubTrue */ stubTrue(): true; } interface LoDashExplicitWrapper<TValue> { /** * @see _.stubTrue */ stubTrue(): LoDashExplicitWrapper<true>; } // stubFalse interface LoDashStatic { /** * This method returns false. * * @return Returns false. */ stubFalse(): false; } interface LoDashImplicitWrapper<TValue> { /** * @see _.stubFalse */ stubFalse(): false; } interface LoDashExplicitWrapper<TValue> { /** * @see _.stubFalse */ stubFalse(): LoDashExplicitWrapper<false>; } } 报A rest parameter must be of an array type.错误
最新发布
07-31
------------------------ Bancai.java ------------------------ package com.kucun.data.entity; import java.lang.annotation.Annotation; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.OneToOne; import javax.persistence.Table; import com.kucun.data.entity.DTO.*; import com.fasterxml.jackson.annotation.JsonBackReference; import com.fasterxml.jackson.annotation.JsonManagedReference; import com.fasterxml.jackson.databind.annotation.JsonSerialize; /** 板材 @author Administrator */ @Entity @Table(name=“bancai”) @JsonSerialize(using = FullEntitySerializer.class) @UniqueEntity( repositoryName = “bancai”, fields = {“houdu”, “caizhi”, “mupi1”, “mupi2”}, message = “板材组合已存在” ) public class Bancai extends EntityBasis { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @ManyToOne( fetch = FetchType.LAZY) @JoinColumn(name = “caizhi_id”) // private Caizhi caizhi; @ManyToOne( fetch = FetchType.LAZY) @JoinColumn(name = “mupi1_id”) private Mupi mupi1; @ManyToOne( fetch = FetchType.LAZY) @JoinColumn(name = “mupi2_id”) private Mupi mupi2; private Double houdu; @OneToOne( cascade = CascadeType.ALL, orphanRemoval = true, // 添加此配置 fetch = FetchType.LAZY ) @JoinColumn(name = “kucun_id”, referencedColumnName = “id”) private Kucun kucun; public Kucun getKucun() { return kucun; } public void setKucun(Kucun kucun) { this.kucun = kucun; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Caizhi getCaizhi() { return caizhi; } public void setCaizhi(Caizhi caizhi) { this.caizhi = caizhi; } public Mupi getMupi1() { return mupi1; } public void setMupi1(Mupi mupi1) { this.mupi1 = mupi1; } public Mupi getMupi2() { return mupi2; } public void setMupi2(Mupi mupi2) { this.mupi2 = mupi2; } public Double getHoudu() { return houdu; } public void setHoudu(Double houdu) { this.houdu = houdu; } public Bancai(int id, Caizhi caizhi, Mupi mupi1, Mupi mupi2, Double houdu) { super(); this.id = id; this.caizhi = caizhi; this.mupi1 = mupi1; this.mupi2 = mupi2; this.houdu = houdu; } public Bancai() { super(); } } ------------------------ Caizhi.java ------------------------ package com.kucun.data.entity; import java.util.List; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.OneToMany; import javax.persistence.Table; import javax.persistence.UniqueConstraint; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.kucun.data.entity.DTO.FullEntitySerializer; import com.kucun.data.entity.DTO.UniqueEntity; /** 板材材质 @author Administrator */ @Entity @Table(name=“caizhi”, uniqueConstraints = { @UniqueConstraint(columnNames = “name”) }) @UniqueEntity( repositoryName = “caizhi”, fields = {“name”}, message = “材质已存在” ) @JsonSerialize(using = FullEntitySerializer.class) public class Caizhi extends EntityBasis{ @OneToMany(mappedBy="caizhi") private List<Bancai> bancai; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public List<Bancai> getBancai() { return bancai; } public void setBancai(List<Bancai> bancai) { this.bancai = bancai; } } ------------------------ Chanpin.java ------------------------ package com.kucun.data.entity; import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.OneToMany; import javax.persistence.Table; import javax.persistence.UniqueConstraint; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.kucun.data.entity.DTO.*; /** 产品类 @author Administrator */ @Entity @Table(name=“chanpin”, uniqueConstraints = { @UniqueConstraint(columnNames = “bianhao”) }) @JsonSerialize(using = FullEntitySerializer.class) @UniqueEntity( repositoryName = “chanpin”, fields = {“bianhao”}, message = “该产品已存在” ) public class Chanpin extends EntityBasis { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; // 关联订单产品 @OneToMany( mappedBy = “chanpin”, cascade = CascadeType.ALL, fetch = FetchType.LAZY ) private List<Dingdan_chanpin> dingdan_chanpin; private String bianhao; @OneToMany( mappedBy = "chanpin", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true ) private List<Chanpin_zujian> chanpin_zujian; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getBianhao() { return bianhao; } public void setBianhao(String bianhao) { this.bianhao = bianhao; } public List<Dingdan_chanpin> getDingdan_chanpin() { return dingdan_chanpin; } public void setDingdan_chanpin(List<Dingdan_chanpin> dingdan_chanpin) { this.dingdan_chanpin = dingdan_chanpin; } public List<Chanpin_zujian> getChanpin_zujian() { return chanpin_zujian; } public void setChanpin_zujian(List<Chanpin_zujian> chanpin_zujian) { this.chanpin_zujian = chanpin_zujian; } } ------------------------ Chanpin_zujian.java ------------------------ package com.kucun.data.entity; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.kucun.data.entity.DTO.FullEntitySerializer; import com.kucun.data.entity.DTO.UniqueEntity; /** 产品组件关联类 @author Administrator */ @Entity @Table(name=“chanpin_zujian”) @JsonSerialize(using = FullEntitySerializer.class) @UniqueEntity( repositoryName = “chanpin_zhujian”, fields = { “chanpin”, “zujian”}, message = “产品下也有该组件” ) public class Chanpin_zujian extends EntityBasis { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; // 关联到产品 @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = “chanpin_id”) private Chanpin chanpin; // 关联到组件 @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "zujian_id") private Zujian zujian; // 关联到板材 @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "bancai_id") private Bancai bancai; //一张板材生产多少组件 private Double one_howmany; //生产多少组件 private Double zujianshu; public Double getZujianshu() { return zujianshu; } public void setZujianshu(Double zujianshu) { this.zujianshu = zujianshu; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Chanpin getChanpin() { return chanpin; } public void setChanpin(Chanpin chanpin) { this.chanpin = chanpin; } public Zujian getZujian() { return zujian; } public void setZujian(Zujian zujian) { this.zujian = zujian; } public Bancai getBancai() { return bancai; } public void setBancai(Bancai bancai) { this.bancai = bancai; } public Double getOne_howmany() { return one_howmany; } public void setOne_howmany(Double one_howmany) { this.one_howmany = one_howmany; } public Chanpin_zujian() { super(); // TODO Auto-generated constructor stub } } ------------------------ Dingdan.java ------------------------ package com.kucun.data.entity; import java.util.Date; import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.ManyToMany; import javax.persistence.OneToMany; import javax.persistence.Table; import javax.persistence.UniqueConstraint; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.kucun.data.entity.DTO.FullEntitySerializer; import com.kucun.data.entity.DTO.UniqueEntity; /** 订单 @author Administrator */ @Entity @Table(name=“dingdan”, uniqueConstraints = { @UniqueConstraint(columnNames = “number”) }) @UniqueEntity( repositoryName = “dingdan”, fields = {“numder”}, message = “该订单已存在” ) @JsonSerialize(using = FullEntitySerializer.class) public class Dingdan extends EntityBasis{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; //订单号 private String number; private Date xiadan; private Date jiaohuo; @OneToMany( mappedBy = “dingdan”, cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true ) private List<Dingdan_chanpin> dingdan_chanpin; public Date getXiadan() { return xiadan; } public void setXiadan(Date xiadan) { this.xiadan = xiadan; } public Date getJiaohuo() { return jiaohuo; } public void setJiaohuo(Date jiaohuo) { this.jiaohuo = jiaohuo; } public List<Dingdan_chanpin> getDingdan_chanpin() { return dingdan_chanpin; } public void setDingdan_chanpin(List<Dingdan_chanpin> dingdan_chanpins) { this.dingdan_chanpin = dingdan_chanpins; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } public Dingdan(Integer id, String number) { super(); this.id = id; this.number = number; } public Dingdan() { super(); // TODO Auto-generated constructor stub } } ------------------------ Dingdan_chanpin.java ------------------------ package com.kucun.data.entity; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.kucun.data.entity.DTO.FullEntitySerializer; import com.kucun.data.entity.DTO.UniqueEntity; /** 订单和产品关联 @author Administrator */ @Entity @Table(name=“dingdan_chanpin”) @UniqueEntity( repositoryName = “dingdan_chanpin”, fields = {“dingdan”,“chanpin”}, message = “订单下已有该产品” ) @JsonSerialize(using = FullEntitySerializer.class) public class Dingdan_chanpin extends EntityBasis { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; //产品信息 @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = “dingdan_id”) // 指 private Dingdan dingdan; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "chanpin_id") // 指 private Chanpin chanpin; private Integer shuliang;//产品数量; public Chanpin getChanpin() { return chanpin; } public void setChanpin(Chanpin chanpin) { this.chanpin = chanpin; } public Integer getShuliang() { return shuliang; } public void setShuliang(Integer shuliang) { this.shuliang = shuliang; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Dingdan getDingdan() { return dingdan; } public void setDingdan(Dingdan dingdan) { this.dingdan = dingdan; } public Chanpin getChanping() { return chanpin; } public void setChanping(Chanpin chanping) { this.chanpin = chanping; } } ------------------------ EntityBasis.java ------------------------ package com.kucun.data.entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.MappedSuperclass; @MappedSuperclass public abstract class EntityBasis implements EntityBasisId { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } } ------------------------ EntityBasisId.java ------------------------ package com.kucun.data.entity; public interface EntityBasisId { Integer getId(); void setId(Integer id); } ------------------------ Information.java ------------------------ package com.kucun.data.entity; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; /** 通信类 @author Administrator */ public class Information { private static final ObjectMapper mapper = new ObjectMapper(); private Integer Status ; private String text; private Object data; public Integer getStatus() { return Status; } public void setStatus(Integer status) { Status = status; } public String getText() { return text; } public void setText(String text) { this.text = text; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } public Information(Integer status, String text, Object data) { super(); Status = status; this.text = text; this.data = data; } @SuppressWarnings({“unchecked”,“rawtypes”}) public Information(Integer status, String text, String data, Class T) throws Exception { super(); Status = status; this.text = text; this.data = fromJson(data,T); } public Information() { super(); // TODO Auto-generated constructor stub } public String DataJson() throws JsonProcessingException { // Java对象转JSON return mapper.writeValueAsString(this); } @SuppressWarnings(“unchecked”) public T fromJson(String json, Class clazz) throws Exception { data= mapper.readValue(json, clazz); return (T) data; } public static Information NewSuccess(Object data) { return new Information(200, "success", data); } public static Information NewSuccess(String data) { return new Information(200, "success", data); } public static Information Newfail(Integer status,String text,Object data) { return new Information(status, "success", data); } public static Information NewFail(int i, String string) { // TODO Auto-generated method stub return new Information(i,string,null); } public static Information NewFail( String string) { // TODO Auto-generated method stub return new Information(400,string,null); } } ------------------------ Jinhuo.java ------------------------ package com.kucun.data.entity; import java.util.Date; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.ManyToOne; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.kucun.data.entity.DTO.FullEntitySerializer; @Entity @JsonSerialize(using = FullEntitySerializer.class) public class Jinhuo extends EntityBasis{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @ManyToOne(optional=true) private Dingdan dingdan; @ManyToOne(optional=true) private Chanpin chanpin; @ManyToOne(optional=true) private Zujian zujian; @ManyToOne(optional=true) private Bancai bancai; private Integer shuliang; private Date date; @ManyToOne private User user; public Jinhuo(Integer id, Dingdan dingdan, Chanpin chanpin, Zujian zujian, Bancai bancai, Integer shuliang, Date date, User user) { super(); this.id = id; this.dingdan = dingdan; this.chanpin = chanpin; this.zujian = zujian; this.bancai = bancai; this.shuliang = shuliang; this.date = date; this.user = user; } public Jinhuo() { super(); // TODO Auto-generated constructor stub } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Dingdan getDingdan() { return dingdan; } public void setDingdan(Dingdan dingdan) { this.dingdan = dingdan; } public Chanpin getChanpin() { return chanpin; } public void setChanpin(Chanpin chanpin) { this.chanpin = chanpin; } public Zujian getZujian() { return zujian; } public void setZujian(Zujian zujian) { this.zujian = zujian; } public Bancai getBancai() { return bancai; } public void setBancai(Bancai bancai) { this.bancai = bancai; } public Integer getShuliang() { return shuliang; } public void setShuliang(Integer shuliang) { this.shuliang = shuliang; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } } ------------------------ Kucun.java ------------------------ package com.kucun.data.entity; import java.util.List; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.OneToOne; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.kucun.data.entity.DTO.FullEntitySerializer; /** 库存 @author Administrator */ @Entity @JsonSerialize(using = FullEntitySerializer.class) public class Kucun extends EntityBasis{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private Integer shuliang; @OneToOne(fetch = FetchType.LAZY) // 正确映射 Bancai 实体 @JoinColumn(name = "bancai_id", referencedColumnName = "id") private Bancai bancai; // 新增:库存归属订单(可为空) @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "dingdan_id") private Dingdan reservedOrder; public Integer getId() { return id; } public Dingdan getReservedOrder() { return reservedOrder; } public void setReservedOrder(Dingdan reservedOrder) { this.reservedOrder = reservedOrder; } public void setId(Integer id) { this.id = id; } public Bancai getBancai() { return bancai; } public void setBancai(Bancai bancai) { this.bancai = bancai; } public Integer getShuliang() { return shuliang; } public void setShuliang(Integer shuliang) { this.shuliang = shuliang; } public Kucun(Integer id, Bancai bancai, Integer shuliang) { super(); this.id = id; this.bancai = bancai; this.shuliang = shuliang; } public Kucun() { super(); // TODO Auto-generated constructor stub } } ------------------------ Mupi.java ------------------------ package com.kucun.data.entity; import java.util.List; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.ManyToMany; import javax.persistence.OneToMany; import javax.persistence.Table; import javax.persistence.UniqueConstraint; import org.hibernate.annotations.Type; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.kucun.data.entity.DTO.FullEntitySerializer; /** 木皮 @author Administrator */ @Entity @Table(name=“mupi”, uniqueConstraints = { @UniqueConstraint(columnNames = “name”) }) @JsonSerialize(using = FullEntitySerializer.class) public class Mupi extends EntityBasis{ /** * 是否有油漆 */ @Column(name="you") @Type(type = "org.hibernate.type.BooleanType") private Boolean you; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } // 添加 OneToMany 映射 @OneToMany(mappedBy = "mupi1") // 指向 Bancai 中的 mupi1 字段 private List<Bancai> bancaisForMupi1; @OneToMany(mappedBy = "mupi2") // 指向 Bancai 中的 mupi2 字段 private List<Bancai> bancaisForMupi2; public List<Bancai> getBancaisForMupi1() { return bancaisForMupi1; } public void setBancaisForMupi1(List<Bancai> bancaisForMupi1) { this.bancaisForMupi1 = bancaisForMupi1; } public List<Bancai> getBancaisForMupi2() { return bancaisForMupi2; } public void setBancaisForMupi2(List<Bancai> bancaisForMupi2) { this.bancaisForMupi2 = bancaisForMupi2; } public Mupi() { super(); } public Boolean getYou() { return you; } public void setYou(Boolean you) { this.you = you; } } ------------------------ SimpleEntity.java ------------------------ package com.kucun.data.entity; import javax.persistence.Column; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.MappedSuperclass; @MappedSuperclass public abstract class SimpleEntity implements EntityBasisId{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @Column(nullable = false, unique = true) private String name; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } // Getters and Setters... } ------------------------ User.java ------------------------ package com.kucun.data.entity; import java.util.Objects; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.kucun.data.entity.DTO.FullEntitySerializer; /** 用户 @author Administrator / @Entity @Table(name=“user”) @JsonSerialize(using = FullEntitySerializer.class) public class User extends EntityBasis{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; /* * 名字 / @Column(nullable=false) private String name; /* * 账号 / @Column(nullable=false) private String andy; /* * 密码 / @Column(nullable=false) private String pass; /* * 权限 */ @Column(nullable=false) private int role; public User() { super(); } public User(int id, String name, String andy, String pass) { super(); this.id = id; this.name = name; this.andy = andy; this.pass = pass; } public Integer getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAndy() { return andy; } public void setAndy(String andy) { this.andy = andy; } public String getPass() { return pass; } public void setPass(String pass) { this.pass = pass; } public int getRole() { System.out.println(role); return role; } public void setRole(int role) { this.role = role; } @Override public String toString() { return "{id:" + id + ", name:" + name + ", andy:" + andy + ", pass:" + pass + ", role:" + role + "}"; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; return Objects.equals(id, user.id) && Objects.equals(name, user.name) && Objects.equals(andy, user.andy)&& Objects.equals(role, user.role); //添加所有属性比较 } @Override public int hashCode() { return Objects.hash(id, name, andy,pass,role); } @Override public void setId(Integer id) { // TODO Auto-generated method stub } } ------------------------ Zujian.java ------------------------ package com.kucun.data.entity; import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.Id; import javax.persistence.OneToMany; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.kucun.data.entity.DTO.FullEntitySerializer; @Entity @JsonSerialize(using = FullEntitySerializer.class) public class Zujian extends EntityBasis{ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } // 反向关联到产品组件 @OneToMany( mappedBy = "zujian", cascade = CascadeType.ALL, fetch = FetchType.LAZY ) private List<Chanpin_zujian> chanping_zujian; public List<Chanpin_zujian> getChanping_zujian() { return chanping_zujian; } public void setChanping_zujian(List<Chanpin_zujian> chanping_zujian) { this.chanping_zujian = chanping_zujian; } } ------------------------ DataManager.js ------------------------ //{ 实体类的关联和属性列表 // “entities”: { // “Dingdan”: { // “properties”: { // “id”: “Integer”, // “number”: “String”, // “xiadan”: “Date”, // “jiaohuo”: “Date”, // “dingdan_chanpins”: “List<Dingdan_chanpin> (OneToMany)”, // “dingdan_chanpins_zujians”: “List<Dingdan_chanpin_zujian> (OneToMany)” // }, // “relations”: [ // “关联订单产品(Dingdan_chanpin)”, // “关联订单组件(Dingdan_chanpin_zujian)” // ] // }, // “Dingdan_chanpin”: { // “properties”: { // “id”: “Integer”, // “shuliang”: “Integer” // }, // “relations”: [ // “多对一关联订单(Dingdan)”, // “多对一关联产品(Chanpin)” // ] // }, // “Dingdan_chanpin_zujian”: { // “properties”: { // “id”: “Integer”, // “shuliang”: “Integer” // }, // “relations”: [ // “多对一关联订单(Dingdan)”, // “多对一关联组件(Chanpin_zujian)”, // “多对一关联板材(Bancai)” // ] // }, // “Jinhuo”: { // “properties”: { // “id”: “Integer”, // “shuliang”: “Integer”, // “date”: “Date” // }, // “relations”: [ // “多对一关联订单(Dingdan)”, // “多对一关联产品(Chanpin)”, // “多对一关联组件(Zujian)”, // “多对一关联板材(Bancai)”, // “多对一关联用户(User)” // ] // }, // “Kucun”: { // “properties”: { // “id”: “Integer”, // “shuliang”: “Long” // }, // “relations”: [ // “一对一关联板材(Bancai)” // ] // }, // “Mupi”: { // “properties”: { // “id”: “Integer”, // “name”: “String”, // “you”: “Boolean” // }, // “relations”: [ // “被板材关联(Bancai - mupi1/mupi2)” // ] // }, // “User”: { // “properties”: { // “id”: “Integer”, // “name”: “String”, // “andy”: “String”, // “pass”: “String”, // “role”: “int” // } // }, // “Zujian”: { // “properties”: { // “id”: “Integer”, // “name”: “String” // }, // “relations”: [ // “一对多关联产品组件(Chanpin_zujian)” // ] // }, // “Bancai”: { // “properties”: { // “id”: “Integer”, // “houdu”: “Double” // }, // “relations”: [ // “多对一关联材质(Caizhi)”, // “多对一关联木皮(Mupi - mupi1/mupi2)”, // “一对一关联库存(Kucun)” // ] // }, // “Caizhi”: { // “properties”: { // “id”: “Integer”, // “name”: “String” // }, // “relations”: [ // “一对多关联板材(Bancai)” // ] // }, // “Chanpin”: { // “properties”: { // “id”: “Integer”, // “bianhao”: “String” // }, // “relations”: [ // “一对多关联订单产品(Dingdan_chanpin)”, // “一对多关联产品组件(Chanpin_zujian)” // ] // }, // “Chanpin_zujian”: { // “properties”: { // “id”: “Integer”, // “one_howmany”: “Double” // }, // “relations”: [ // “多对一关联产品(Chanpin)”, // “多对一关联组件(Zujian)”, // “多对一关联板材(Bancai)” // ] // } // }, // “relationsSummary”: [ // “订单(Dingdan) 1:N 订单产品(Dingdan_chanpin)”, // “订单(Dingdan) 1:N 订单组件(Dingdan_chanpin_zujian)”, // “产品(Chanpin) 1:N 产品组件(Chanpin_zujian)”, // “组件(Zujian) 1:N 产品组件(Chanpin_zujian)”, // “板材(Bancai) 1:1 库存(Kucun)”, // “材质(Caizhi) 1:N 板材(Bancai)” // ] //} /** 优化后的关联解析函数(解决空值问题并支持多层关联) @param {Object} data - 后端原始数据 @returns {Object} 处理后的完整关联数据 */ function resolveDataReferences(data) { // 创建ID映射表(带空值保护) const idMaps = {}; Object.keys(data).forEach(key => { if (Array.isArray(data[key])) { idMaps[key] = new Map(); data[key].forEach(item => item.id && idMaps[key].set(item.id, item)); } }); // 通用关联解析方法(支持多层关联) const resolveRef = (sourceArray, sourceKey, targetKey, refProperty) => { if (!Array.isArray(sourceArray)) return; sourceArray.forEach(item => { const refObj = item[refProperty]; if (refObj && refObj.id && idMaps[targetKey]) { const target = idMaps[targetKey].get(refObj.id); if (target) { // 建立正向引用 item[refProperty] = target; // 建立反向引用(自动创建关联数组) const reverseProp = sourceKey.endsWith('s') ? sourceKey.slice(0, -1) + '_list' : sourceKey + '_list'; if (!target[reverseProp]) target[reverseProp] = []; if (!target[reverseProp].includes(item)) { target[reverseProp].push(item); } } } }); }; // 处理所有定义的关联关系 // 订单 ↔ 订单产品 if (data.dingdans && data.dingdan_chanpins) { resolveRef(data.dingdan_chanpins, ‘dingdans’, ‘dingdans’, ‘dingdan’); } // 订单 ↔ 订单组件 if (data.dingdans && data.dingdan_chanpin_zujians) { resolveRef(data.dingdan_chanpin_zujians, ‘dingdans’, ‘dingdans’, ‘dingdan’); } // 产品 ↔ 产品组件 if (data.chanpins && data.chanpin_zujians) { resolveRef(data.chanpin_zujians, ‘chanpins’, ‘chanpins’, ‘chanpin’); } // 组件 ↔ 产品组件 if (data.zujians && data.chanpin_zujians) { resolveRef(data.chanpin_zujians, ‘zujians’, ‘zujians’, ‘zujian’); } // 材质 ↔ 板材 if (data.caizhis && data.bancais) { resolveRef(data.bancais, ‘caizhis’, ‘caizhis’, ‘caizhi’); } // 板材 ↔ 库存(一对一) if (data.bancais && data.kucuns) { resolveRef(data.bancais, ‘kucuns’, ‘kucuns’, ‘kucun’); resolveRef(data.kucuns, ‘bancais’, ‘bancais’, ‘bancai’); } // 板材 ↔ 木皮(mupi1/mupi2) if (data.bancais && data.mupis) { resolveRef(data.bancais, ‘mupis’, ‘mupis’, ‘mupi1’); resolveRef(data.bancais, ‘mupis’, ‘mupis’, ‘mupi2’); } // 订单产品 ↔ 产品 if (data.dingdan_chanpins && data.chanpins) { resolveRef(data.dingdan_chanpins, ‘chanpins’, ‘chanpins’, ‘chanpin’); } // 订单组件 ↔ 产品组件 if (data.dingdan_chanpin_zujians && data.chanpin_zujians) { resolveRef(data.dingdan_chanpin_zujians, ‘chanpin_zujians’, ‘chanpin_zujians’, ‘chanpin_zujian’); } // 订单组件 ↔ 板材 if (data.dingdan_chanpin_zujians && data.bancais) { resolveRef(data.dingdan_chanpin_zujians, ‘bancais’, ‘bancais’, ‘bancai’); } // 产品组件 ↔ 板材 if (data.chanpin_zujians && data.bancais) { resolveRef(data.chanpin_zujians, ‘bancais’, ‘bancais’, ‘bancai’); } // 进货 ↔ 相关实体 if (data.jinhuos) { [‘dingdan’, ‘chanpin’, ‘zujian’, ‘bancai’, ‘user’].forEach(entity => { const plural = entity + ‘s’; if (data[plural]) { resolveRef(data.jinhuos, plural, plural, entity); } }); } // 新增:计算订单可用板材 if (data.dingdans) { data.dingdans.forEach(dingdan => { // 初始化订单可用板材 dingdan.availableBancais = {}; // 遍历订单中的所有产品 if (dingdan.dingdan_chanpin_list) { dingdan.dingdan_chanpin_list.forEach(dc => { if (dc.chanpin && dc.chanpin.chanpin_zujian_list) { // 遍历产品的所有组件 dc.chanpin.chanpin_zujian_list.forEach(cz => { if (cz.bancai) { const bancaiId = cz.bancai.id; // 计算组件所需板材数量 = 产品数量 × 组件数量比例 const required = dc.shuliang * (cz.zujianshu || 1); if (!dingdan.availableBancais[bancaiId]) { dingdan.availableBancais[bancaiId] = { bancai: cz.bancai, required: 0, available: 0 }; } dingdan.availableBancais[bancaiId].required += required; } }); } }); } // 遍历订单中的所有直接组件 if (dingdan.dingdan_chanpin_zujian_list) { dingdan.dingdan_chanpin_zujian_list.forEach(dz => { if (dz.bancai) { const bancaiId = dz.bancai.id; const required = dz.shuliang; if (!dingdan.availableBancais[bancaiId]) { dingdan.availableBancais[bancaiId] = { bancai: dz.bancai, required: 0, available: 0 }; } dingdan.availableBancais[bancaiId].required += required; } }); } // 计算可用数量(从库存中减去其他订单预留量) Object.values(dingdan.availableBancais).forEach(info => { if (info.bancai.kucun) { // 获取板材总库存 let totalAvailable = info.bancai.kucun.shuliang; // 减去其他订单预留量 data.dingdans.forEach(otherDingdan => { if (otherDingdan.id !== dingdan.id && otherDingdan.availableBancais?.[info.bancai.id]) { totalAvailable -= otherDingdan.availableBancais[info.bancai.id].required; } }); // 确保可用量不小于0 info.available = Math.max(0, totalAvailable); } }); }); } return data; } function _resolveDataReferences(data) { // 获取 data 对象的所有顶层键 const keys = Object.keys(data); // 遍历每个顶层键(如 users, posts 等) for (const key of keys) { const entities = data[key]; // 遍历该顶层键下的每个实体(如每个 user 或 post) for (const entity of entities) { // 遍历实体的每个属性 for (const attribute in entity) { if (entity.hasOwnProperty(attribute)) { var trpe=attribute.replace(/\d/g, ''); // 确保属性属于当前实体 if (Array.isArray(entity[attribute])) { if(data[trpe]==null){ trpe+="s" } // 如果属性是一个数组,则将数组中的每个 ID 替换为对应的实际对象 entity[attribute] = entity[attribute].map(item => data[trpe ]?.find(updateItem => updateItem.id === item.id) || item ); } else if (typeof entity[attribute] === "object" && entity[attribute] !== null) { // 如果属性是一个对象,则将其替换为对应的实际对象 entity[attribute] = data[trpe + "s"]?.find(updateItem => updateItem.id === entity[attribute].id); } } } } } console.log(data) return data; } /** 数据管理器类,负责与后端API通信并管理数据 */ class DataManager { constructor(baseUrl) { this.baseUrl = baseUrl; this.data = { bancais: [], dingdans: [], mupis: [], chanpins: [], kucuns: [], chanpin_zujians: [], zujians: [], caizhis: [], dingdan_chanpins: [], users: [], jinhuos: [] }; this.isSyncing = false; this.lastSync = null; this.callbacks = { all: [], bancais: [], dingdan: [], mupi: [], chanpin: [], kucun: [], chanpin_zujian: [], zujian: [], caizhi: [], dingdan_chanpin: [], user: [], jinhuo: [] }; this.syncQueue = Promise.resolve(); } /** 获取所有数据 @returns {Promise} 是否成功 */ async fetchAll() { console.log(this) try { const response = await fetch(${this.baseUrl}/app/all); if (!response.ok) throw new Error(‘Network response was not ok’); const result = await response.json(); if (result.status !== 200) throw new Error(result.text || ‘API error’); const resolvedData = resolveDataReferences(result.data); // 更新本地数据 Object.keys(this.data).forEach(key => { if (resolvedData[key]) { this.data[key] = resolvedData[key]; } }); this.lastSync = new Date(); // 关键改进:数据更新后触发刷新回调 this.triggerCallbacks(‘refresh’, ‘all’, this.data); return true; } catch (error) { console.error(‘Fetch error:’, error); // 触发错误回调 this.triggerCallbacks(‘fetch_error’, ‘all’, { error }); return false; } } /** 注册回调函数 @param {string} entity - 实体类型(如’bancai’)或’all’表示全局回调 @param {Function} callback - 回调函数,参数为(operation, data) */ registerCallback(entity, callback) { if (!this.callbacks[entity]) { this.callbacks[entity] = []; } this.callbacks[entity].push(callback); } /** 移除回调函数 @param {string} entity - 实体类型单数性质 @param {Function} callback - 要移除的回调函数 */ unregisterCallback(entity, callback) { if (!this.callbacks[entity]) return; const index = this.callbacks[entity].indexOf(callback); if (index !== -1) { this.callbacks[entity].splice(index, 1); } } /** 触发回调 @param {string} operation - 操作类型(‘add’, ‘update’, ‘delete’) @param {string} entity - 实体类型单数性质 @param {Object} data - 相关数据 */ triggerCallbacks(operation, entity, data) { // 触发全局回调 this.callbacks.all.forEach(cb => cb(operation, entity, data)); // 触发特定实体回调 if (this.callbacks[entity]) { this.callbacks[entity].forEach(cb => cb(operation, data)); } } /** 执行CRUD操作并触发回调 */ async crudOperation(operation, entity, data) { try { const response = await fetch(${this.baseUrl}/app/${operation}/${entity}, { method: ‘POST’, headers: {‘Content-Type’: ‘application/json’}, body: JSON.stringify(data) }); if (!response.ok) throw new Error(‘Network response was not ok’); const result = await response.json(); if (result.status !== 200) throw new Error(result.text || ‘API error’); // 自动同步数据 this.syncData(); // 触发操作成功的回调 this.triggerCallbacks(operation, entity, data); return result; } catch (error) { console.error(‘CRUD error:’, error); // 触发操作失败的回调 this.triggerCallbacks(${operation}_error, entity, { data, error: error.message }); throw error; } } /** 执行CRUD操作 @param {string} operation - ‘add’, ‘delete’, ‘update’ @param {string} entity - 实体名称单数性质(小写) @param {Object} data - 要发送的数据 后端要求数据格式为{属性: “值”, 关联对象: {id:0}, 关联对象集: [{id:0}]} @returns {Promise} 响应结果 */ async crudOperation(operation, entity, data) { try { const response = await fetch(${this.baseUrl}/app/${operation}/${entity}, { method: ‘POST’, headers: {‘Content-Type’: ‘application/json’}, body: JSON.stringify(data) }); if (!response.ok) throw new Error('Network response was not ok'); const result = await response.json(); if (result.status !== 200) throw new Error(result.text || 'API error'); // 自动同步数据 this.syncQueue = this.syncQueue.then(async () => { await this.syncData(); // 同步完成后触发操作回调 this.triggerCallbacks(operation, entity, data); }); return result; } catch (error) { console.error(‘CRUD error:’, error); // 触发操作失败的回调 this.triggerCallbacks(${operation}_error, entity, { data, error: error.message }); throw error; } } /** 自动同步数据(防止频繁请求) */ async syncData() { if (this.isSyncing) { this.pendingSync = true; return; } this.isSyncing = true; try { await this.fetchAll(); } catch (error) { console.error(‘Sync failed:’, error); } finally { this.isSyncing = false; // 处理等待中的同步请求 if (this.pendingSync) { this.pendingSync = false; setTimeout(() => this.syncData(), 1000); } } } /** 添加实体 @param {string} entity - 实体名称单数性质 @param {Object} data - 实体数据 */ async addEntity(entity, data) { return this.crudOperation(‘add’, entity, data); } /** 更新实体 @param {string} entity - 实体名称单数性质 @param {Object} data - 实体数据(必须包含id) */ async updateEntity(entity, data) { return this.crudOperation(‘update’, entity, data); } /** 删除实体 @param {string} entity - 实体名称单数性质 @param {number} id - 实体ID */ async deleteEntity(entity, id) { return this.crudOperation(‘delete’, entity, {id}); } /** 新增方法:手动触发数据刷新 */ async refreshData() { return this.syncQueue = this.syncQueue.then(() => this.syncData()); } } export { DataManager }; // 创建单例实例 //const dataManager = new DataManager(‘http://127.0.0.1:8080/KuCun2’); //// 初始化时获取所有数据 //dataManager.fetchAll().then(() => { // console.log(‘Initial data loaded’); //}); // 导出数据对象,外部可以直接访问 data.bancais, data.dingdans 等 //export const data = dataManager.data; //// 导出操作方法 //export const addEntity = dataManager.addEntity.bind(dataManager); //export const updateEntity = dataManager.updateEntity.bind(dataManager); //export const deleteEntity = dataManager.deleteEntity.bind(dataManager); //export const fetchAll = dataManager.fetchAll.bind(dataManager); ------------------------ dingdan.js ------------------------ // 监听来自父窗口的消息 window.addEventListener(‘message’, function(event) { if (event.source === window.parent && event.data === ‘DataManagerReady’) { initializeDataManager(); } }); // 初始化数据管理器 function initializeDataManager() { const dataManager = window.parent.dataManager; if (dataManager) { // 注册数据刷新回调 dataManager.registerCallback('all', function(operation, entity, data) { updatePageData(dataManager); }); // 初始页面数据更新 updatePageData(dataManager); // 设置搜索功能 setupSearch(dataManager); // 设置排序功能 setupSorting(); } else { console.error('DataManager not available'); } } // 更新页面数据 function updatePageData(dataManager) { updateStats(dataManager); updateTable(dataManager); document.getElementById(‘lastUpdate’).textContent = new Date().toLocaleTimeString(); } // 更新统计卡片数据 function updateStats(dataManager) { const data = dataManager.data; document.getElementById('orderCount').textContent = data.dingdans?.length || 0; document.getElementById('productCount').textContent = data.chanpins?.length || 0; document.getElementById('materialCount').textContent = data.bancais?.length || 0; const totalStock = data.kucuns?.reduce((sum, kucun) => sum + kucun.shuliang, 0) || 0; document.getElementById('totalStock').textContent = totalStock; } // 全局变量存储当前排序状态 let currentSort = { column: null, direction: ‘asc’ // ‘asc’ 或 ‘desc’ }; // 更新结果表格 function updateTable(dataManager) { const tableBody = document.getElementById(‘resultBody’); tableBody.innerHTML = ‘’; const data = dataManager.data; let results = []; // 改为let以便排序 let resultCount = 0; // 处理订单产品数据 if (data.dingdan_chanpins) { data.dingdan_chanpins.forEach(dc => { if (!dc.dingdan || !dc.chanpin) return; if (dc.chanpin.chanpin_zujians) { dc.chanpin.chanpin_zujians.forEach(cz => { if (!cz.zujian || !cz.bancai) return; const bancai = cz.bancai; const materialInfo = getMaterialInfo(bancai); const stockQuantity = dataManager.data.kucuns?.find(ku => ku.bancai?.id==bancai.id)?.shuliang || 0; results.push({ orderNumber: dc.dingdan.number, productInfo: dc.chanpin.bianhao, productQuantity: dc.shuliang, component: cz.zujian.name, material: materialInfo, materialPerComponent: cz.one_howmany, materialOrderQuantity: dc.shuliang * cz.one_howmany, stockQuantity: stockQuantity, // 添加原始数据用于排序 raw: { orderNumber: dc.dingdan.number, productQuantity: dc.shuliang, materialPerComponent: cz.one_howmany, materialOrderQuantity: dc.shuliang * cz.one_howmany, stockQuantity: stockQuantity, thickness: bancai.houdu || 0 }, operation: `<button class="btn btn-sm btn-outline-primary" onclick="showMaterialDetail(${bancai.id})">详情</button>` }); resultCount++; }); } }); } // 处理直接订单组件数据 if (data.dingdan_chanpin_zujians) { data.dingdan_chanpin_zujians.forEach(dcz => { if (!dcz.dingdan || !dcz.chanpin_zujian || !dcz.chanpin_zujian.zujian || !dcz.bancai) return; const bancai = dcz.bancai; const materialInfo = getMaterialInfo(bancai); const stockQuantity = dataManager.data.kucuns?.find(ku => ku.bancai?.id==bancai.id)?.shuliang || 0; results.push({ orderNumber: dcz.dingdan.number, productInfo: dcz.chanpin_zujian.chanpin?.bianhao || '独立组件', productQuantity: dcz.shuliang, component: dcz.chanpin_zujian.zujian.name, material: materialInfo, materialPerComponent: dcz.chanpin_zujian.one_howmany, materialOrderQuantity: dcz.shuliang * dcz.chanpin_zujian.one_howmany, stockQuantity: stockQuantity, // 添加原始数据用于排序 raw: { orderNumber: dcz.dingdan.number, productQuantity: dcz.shuliang, materialPerComponent: dcz.chanpin_zujian.one_howmany, materialOrderQuantity: dcz.shuliang * dcz.chanpin_zujian.one_howmany, stockQuantity: stockQuantity, thickness: bancai.houdu || 0 }, operation: `<button class="btn btn-sm btn-outline-primary" onclick="showMaterialDetail(${bancai.id})">详情</button>` }); resultCount++; }); } // 应用排序 if (currentSort.column !== null) { results = sortResults(results, currentSort.column, currentSort.direction); } // 填充表格 if (resultCount > 0) { document.getElementById('noResults').style.display = 'none'; results.forEach(row => { const tr = document.createElement('tr'); tr.innerHTML = ` <td>${row.orderNumber}</td> <td>${row.productInfo}</td> <td>${row.productQuantity}</td> <td>${row.component}</td> <td>${row.material}</td> <td>${row.materialPerComponent}</td> <td>${row.materialOrderQuantity}</td> <td>${row.stockQuantity}</td> <td>${row.operation}</td> `; tableBody.appendChild(tr); }); } else { document.getElementById('noResults').style.display = 'flex'; } document.getElementById('resultCount').textContent = resultCount; } // 排序函数 function sortResults(results, columnIndex, direction) { return results.sort((a, b) => { // 根据列索引确定排序字段 let field; switch(columnIndex) { case 0: // 订单号 field = ‘orderNumber’; break; case 1: // 产品信息 field = ‘productInfo’; break; case 2: // 产品数量 field = ‘productQuantity’; break; case 3: // 组件 field = ‘component’; break; case 4: // 板材 // 特殊处理:按厚度排序 return sortByThickness(a, b, direction); case 5: // 单件用量 field = ‘materialPerComponent’; break; case 6: // 订单用量 field = ‘materialOrderQuantity’; break; case 7: // 库存数量 field = ‘stockQuantity’; break; default: return 0; } // 数值排序 if (['productQuantity', 'materialPerComponent', 'materialOrderQuantity', 'stockQuantity'].includes(field)) { return sortNumeric(a.raw[field], b.raw[field], direction); } // 字符串排序 return sortString(a[field], b[field], direction); }); } // 按厚度排序 function sortByThickness(a, b, direction) { return sortNumeric(a.raw.thickness, b.raw.thickness, direction); } // 数值排序 function sortNumeric(aVal, bVal, direction) { const aNum = parseFloat(aVal) || 0; const bNum = parseFloat(bVal) || 0; if (direction === 'asc') { return aNum - bNum; } else { return bNum - aNum; } } // 字符串排序 function sortString(aVal, bVal, direction) { const aStr = String(aVal).toLowerCase(); const bStr = String(bVal).toLowerCase(); if (aStr < bStr) { return direction === 'asc' ? -1 : 1; } else if (aStr > bStr) { return direction === 'asc' ? 1 : -1; } return 0; } // 获取板材详细信息函数(添加油状态) function getMaterialInfo(bancai) { if (!bancai) return ‘未知板材’; // 获取材质名称 const caizhiName = bancai.caizhi?.name || '未知材质'; // 获取木皮信息(添加油状态) const formatMupi = (mupi) => { if (!mupi) return null; return `${mupi.name}${mupi.you ? '(油)' : ''}`; }; const mupi1Str = formatMupi(bancai.mupi1) || '无'; const mupi2Str = formatMupi(bancai.mupi2) || '无'; const mupiInfo = mupi1Str + (mupi2Str !== '无' ? `/${mupi2Str}` : ''); // 厚度转换为字符串 const thickness = bancai.houdu ? `${bancai.houdu.toFixed(1)}mm` : '未知厚度'; return `${caizhiName} - ${mupiInfo} (${thickness})`; } // 设置搜索功能(添加油状态搜索) function setupSearch(dataManager) { // 订单搜索 ParseError: KaTeX parse error: Expected 'EOF', got '#' at position 3: ('#̲orderSearch').o…(this).val().toLowerCase(), 0); }); // 产品搜索 $('#productSearch').on('input', function() { filterTable($(this).val().toLowerCase(), 1); }); // 板材材质搜索 $('#materialSearch').on('input', function() { filterTable($(this).val().toLowerCase(), 4); }); // 木皮搜索(添加油状态搜索) $('#woodSearch').on('input', function() { const searchTerm = $(this).val().toLowerCase(); // 支持中文和数字搜索 const searchKeywords = { '有油': '有油', '无油': '无油', 'you': '有油', 'wu': '无油', '1': '有油', '0': '无油', '(油)': '(油)' }; const actualTerm = searchKeywords[searchTerm] || searchTerm; filterTable(actualTerm, 4); }); // 厚度搜索(修复正则表达式) $('#thicknessSearch').on('input', function() { filterTable($(this).val().toLowerCase(), 4); }); // 库存状态搜索 $('#stockStatusBtn').click(function() { const minStock = parseInt($('#minStock').val()) || 0; const maxStock = parseInt($('#maxStock').val()) || Number.MAX_SAFE_INTEGER; filterByStock(minStock, maxStock); }); } // 设置排序功能 function setupSorting() { // 获取所有表头 const headers = document.querySelectorAll(‘#resultsTable th[data-sortable]’); headers.forEach((header, index) => { header.addEventListener('click', () => { // 更新排序状态 if (currentSort.column === index) { // 同一列:切换方向 currentSort.direction = currentSort.direction === 'asc' ? 'desc' : 'asc'; } else { // 新列:默认升序 currentSort.column = index; currentSort.direction = 'asc'; } // 更新UI updateSortIndicators(); // 重新渲染表格 const dataManager = window.parent.dataManager; if (dataManager) { updateTable(dataManager); } }); }); } // 更新排序指示器 function updateSortIndicators() { // 清除所有指示器 const headers = document.querySelectorAll(‘#resultsTable th[data-sortable]’); headers.forEach(header => { header.querySelector(‘.sort-indicator’).textContent = ‘’; }); // 为当前排序列添加指示器 if (currentSort.column !== null) { const currentHeader = headers[currentSort.column]; const indicator = currentHeader.querySelector('.sort-indicator'); indicator.textContent = currentSort.direction === 'asc' ? '↑' : '↓'; } } // 表格过滤函数(修复厚度搜索) function filterTable(searchTerm, columnIndex) { const rows = $(‘#resultBody tr’); let visibleCount = 0; rows.each(function() { const cellText = $(this).find(`td:eq(${columnIndex})`).text().toLowerCase(); let isMatch = false; // 特殊处理油状态搜索 if (searchTerm === '有油' || searchTerm === '无油' || searchTerm === '(油)') { isMatch = cellText.includes(searchTerm); } // 处理厚度字符串匹配(修复正则表达式) else if (columnIndex === 4 && !isNaN(parseFloat(searchTerm))) { // 提取厚度数字部分进行匹配 const thicknessMatch = cellText.match(/(\d+\.?\d*)/); if (thicknessMatch) { const thicknessValue = parseFloat(thicknessMatch[1]); const searchValue = parseFloat(searchTerm); // 允许小数点误差 isMatch = Math.abs(thicknessValue - searchValue) < 0.5; } } // 常规文本匹配 else { isMatch = cellText.includes(searchTerm); } $(this).toggle(isMatch); if (isMatch && searchTerm) { $(this).addClass('highlight'); } else { $(this).removeClass('highlight'); } if (isMatch) visibleCount++; }); document.getElementById('resultCount').textContent = visibleCount; document.getElementById('noResults').style.display = visibleCount > 0 ? 'none' : 'flex'; } // 按库存量过滤 function filterByStock(minStock, maxStock) { const rows = $(‘#resultBody tr’); let visibleCount = 0; rows.each(function() { const stockCell = $(this).find('td:eq(7)').text(); const stockValue = parseInt(stockCell) || 0; const isMatch = stockValue >= minStock && stockValue <= maxStock; $(this).toggle(isMatch); if (isMatch) visibleCount++; }); document.getElementById('resultCount').textContent = visibleCount; document.getElementById('noResults').style.display = visibleCount > 0 ? 'none' : 'flex'; } // 显示板材详情(添加油状态显示) function showMaterialDetail(bancaiId) { const dataManager = window.parent.dataManager; if (!dataManager) return; const bancais = dataManager.data.bancais || []; const bancai = bancais.find(b => b.id === bancaiId); const kucun = dataManager.data.kucuns?.fill(ku => ku.bancai.id==bancai.id) ; if (!bancai) { alert('未找到板材信息'); return; } // 格式化木皮信息(添加油状态) const formatMupiDetail = (mupi) => { if (!mupi) return '无'; return `${mupi.name} ${mupi.you ? '(油)' : ''}`; }; // 构建详情信息 const detailHtml = ` <div class="material-detail"> <h4>板材详情 (ID: ${bancai.id})</h4> <p><strong>材质:</strong> ${bancai.caizhi?.name || '未知'}</p> <p><strong>厚度:</strong> ${bancai.houdu ? bancai.houdu.toFixed(1) + 'mm' : '未知'}</p> <p><strong>木皮1:</strong> ${formatMupiDetail(bancai.mupi1)}</p> <p><strong>木皮2:</strong> ${formatMupiDetail(bancai.mupi2)}</p> <p><strong>库存数量:</strong> ${kucun?.shuliang || 0}</p> <div class="related-orders"> <h5>相关订单:</h5> <ul> ${getRelatedOrders(bancai).map(order => `<li>订单 ${order.number} (ID: ${order.id})</li>` ).join('') || '<li>无相关订单</li>'} </ul> </div> </div> `; // 显示详情(实际项目中可用模态框) const detailWindow = window.open('', '_blank', 'width=600,height=400'); detailWindow.document.write(` <!DOCTYPE html> <html> <head> <title>板材详情</title> <style> body { font-family: Arial, sans-serif; padding: 20px; } .material-detail { max-width: 500px; } h4 { color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 10px; } p { margin: 10px 0; } .related-orders { margin-top: 20px; } ul { list-style-type: none; padding: 0; } li { padding: 5px 0; border-bottom: 1px solid #eee; } </style> </head> <body> ${detailHtml} </body> </html> `); detailWindow.document.close(); } // 获取板材相关订单 function getRelatedOrders(bancai) { const dataManager = window.parent.dataManager; if (!dataManager) return []; const orders = []; const dingdan_chanpin_zujians = dataManager.data.dingdan_chanpin_zujians || []; // 查找直接订单组件关联 dingdan_chanpin_zujians.forEach(dcz => { if (dcz.bancai?.id === bancai.id && dcz.dingdan && !orders.some(o => o.id === dcz.dingdan.id)) { orders.push(dcz.dingdan); } }); // 查找产品组件关联 const chanpin_zujians = dataManager.data.chanpin_zujians || []; chanpin_zujians.forEach(cz => { if (cz.bancai?.id === bancai.id) { const dingdan_chanpins = dataManager.data.dingdan_chanpins || []; dingdan_chanpins.forEach(dc => { if (dc.chanpin?.id === cz.chanpin?.id && dc.dingdan && !orders.some(o => o.id === dc.dingdan.id)) { orders.push(dc.dingdan); } }); } }); return orders; } // 页面加载时尝试初始化 if (window.parent) { window.parent.postMessage(‘RequestDataManager’, ‘*’); } // 添加全局函数供按钮调用 window.showMaterialDetail = showMaterialDetail; ------------------------ test.html ------------------------ <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>板材库存查询系统</title> <!-- 引入外部CSS --> <link rel="stylesheet" href="../css/dingdan.css"> <!-- 引入jQuery --> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <!-- 引入Bootstrap --> <link href="../js/bootstrap-5.3.0-alpha1-dist/css/bootstrap.min.css" rel="stylesheet"> <script src="../js/bootstrap-5.3.0-alpha1-dist/umd/popper.min.js"></script> <script src="../js/bootstrap-5.3.0-alpha1-dist/js/bootstrap.min.js"></script> <!-- 引入Bootstrap Icons --> <link rel="stylesheet" href="../js/bootstrap-icons-1.8.1/bootstrap-icons.css"> <!-- 引入主JS文件 --> <script src="../js/main.js"></script> </head> <body> <div class="container py-4"> <!-- 标题部分 --> <div class="text-center mb-4"> <h1 class="text-primary"><i class="bi bi-boxes"></i> 板材库存管理系统</h1> <p class="text-muted">查询订单、产品、板材及库存信息</p> </div> <!-- 统计卡片 --> <div class="row mb-4"> <div class="col-md-3"> <div class="card stats-card border-primary"> <div class="card-body"> <h5 class="card-title">订单总数</h5> <p class="card-text fs-3 text-primary" id="orderCount">0</p> </div> </div> </div> <div class="col-md-3"> <div class="card stats-card border-info"> <div class="card-body"> <h5 class="card-title">产品种类</h5> <p class="card-text fs-3 text-info" id="productCount">0</p> </div> </div> </div> <div class="col-md-3"> <div class="card stats-card border-success"> <div class="card-body"> <h5 class="card-title">板材库存</h5> <p class="card-text fs-3 text-success" id="materialCount">0</p> </div> </div> </div> <div class="col-md-3"> <div class="card stats-card border-warning"> <div class="card-body"> <h5 class="card-title">库存总量</h5> <p class="card-text fs-3 text-warning" id="totalStock">0</p> </div> </div> </div> </div> <!-- 搜索区域 --> <div class="card search-section mb-4"> <div class="card-header"> <h5 class="mb-0"><i class="bi bi-search me-2"></i>高级搜索</h5> </div> <div class="card-body"> <div class="row g-3"> <!-- 订单搜索 --> <div class="col-md-4"> <div class="search-control"> <i class="bi bi-clipboard-search search-icon"></i> <input type="text" class="form-control with-icon" id="orderSearch" placeholder="搜索订单号..." aria-label="订单号搜索"> </div> </div> <!-- 产品搜索 --> <div class="col-md-4"> <div class="search-control"> <i class="bi bi-grid search-icon"></i> <input type="text" class="form-control with-icon" id="productSearch" placeholder="搜索产品编号..." aria-label="产品编号搜索"> </div> </div> <!-- 板材搜索 --> <div class="col-md-4"> <div class="search-control"> <i class="bi bi-box search-icon"></i> <input type="text" class="form-control with-icon" id="materialSearch" placeholder="搜索板材ID或材质..." aria-label="板材搜索"> </div> </div> <!-- 木皮搜索 --> <div class="col-md-4"> <div class="search-control"> <i class="bi bi-tree search-icon"></i> <input type="text" class="form-control with-icon" id="woodSearch" placeholder="搜索木皮名称..." aria-label="木皮搜索"> </div> </div> <!-- 厚度搜索 --> <div class="col-md-4"> <div class="search-control"> <i class="bi bi-arrows-vertical search-icon"></i> <input type="number" class="form-control with-icon" id="thicknessSearch" placeholder="厚度(mm)" min="0" step="0.1"> </div> </div> <!-- 库存范围搜索 --> <div class="col-md-4"> <div class="input-group"> <span class="input-group-text"><i class="bi bi-box"></i></span> <input type="number" class="form-control" id="minStock" placeholder="最小库存" min="0"> <input type="number" class="form-control" id="maxStock" placeholder="最大库存" min="0"> <button class="btn btn-primary" type="button" id="stockStatusBtn"> <i class="bi bi-search"></i> </button> </div> </div> </div> </div> </div> <!-- 结果区域 --> <div class="card"> <div class="card-header d-flex justify-content-between align-items-center"> <h5 class="mb-0"><i class="bi bi-table me-2"></i>查询结果</h5> <div class="text-secondary"> <span id="resultCount">0</span> 条记录 <span class="ms-2"><i class="bi bi-info-circle"></i> <small>实时数据更新时间: <span id="lastUpdate">--:--:--</span></small></span> </div> </div> <div class="card-body result-section"> <div class="table-responsive"> <table class="table table-hover" id="resultsTable"> <thead class="table-light sticky-top"> <tr> <th data-sortable>订单号 <span class="sort-indicator"></span></th> <th data-sortable>产品信息 <span class="sort-indicator"></span></th> <th data-sortable>产品数量 <span class="sort-indicator"></span></th> <th data-sortable>组件 <span class="sort-indicator"></span></th> <th data-sortable>板材 <span class="sort-indicator"></span></th> <th data-sortable>单件用量 <span class="sort-indicator"></span></th> <th data-sortable>订单用量 <span class="sort-indicator"></span></th> <th data-sortable>库存数量 <span class="sort-indicator"></span></th> <th>操作</th> </tr> </thead> <tbody id="resultBody"> <!-- 数据加载中 --> <tr id="loadingRow"> <td colspan="9" class="text-center py-5"> <div class="d-flex align-items-center justify-content-center"> <div class="spinner-border text-primary" role="status"> <span class="visually-hidden">加载中...</span> </div> <div class="ms-3">正在加载数据,请稍候...</div> </div> </td> </tr> </tbody> </table> </div> <!-- 空结果提示 --> <div id="noResults" class="no-results text-center py-5" style="display: none;"> <div> <i class="bi bi-inboxes text-muted" style="font-size: 3rem;"></i> <h4 class="mt-3 text-muted">没有找到匹配的记录</h4> <p class="text-muted">请尝试调整您的搜索条件</p> </div> </div> </div> </div> </div> <!-- 引入订单JS --> <script src="../js/dingdan.js"></script> </body> </html> 改造页面逻辑,已经删除了dingdan_chanpin_zujian,java和DataManager.js.js是不能改 要求 一个订单有很多产品不同数量,每个产品可以再很多订单中,一个产品有很多组件,一个组件可以在很多产品中,因为每个组件因为在不同的产品中有不同的生产工艺,所以使用不同的板材和板材能生产组件数量,每个板材有不同的材质和两面木皮,木皮表面可能有油漆, 订购时可能直接购入板材,也可能按订单和产品订购板材,也用可能按订单产品组件订购板材,每次采购不标准,一个订单可能订购几次,用户有姓名 账号 密码 权限, 一个记录进货和消耗,查看的时候会查看订单下有多少板材可用
06-23
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值